@manuscripts/body-editor 2.7.7 → 2.7.8-LEAN-4213.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 (72) hide show
  1. package/dist/cjs/components/affiliations/AffiliationForm.js +106 -0
  2. package/dist/cjs/components/affiliations/AffiliationItem.js +112 -0
  3. package/dist/cjs/components/affiliations/AffiliationList.js +47 -0
  4. package/dist/cjs/components/affiliations/AffiliationsModal.js +418 -0
  5. package/dist/cjs/components/authors/AuthorDetailsForm.js +10 -2
  6. package/dist/cjs/components/authors/AuthorList.js +1 -1
  7. package/dist/cjs/components/authors/AuthorsModal.js +184 -80
  8. package/dist/cjs/components/authors/DraggableAuthor.js +0 -3
  9. package/dist/cjs/components/dialog/ConfirmationDialog.js +86 -0
  10. package/dist/cjs/components/form/FormFooter.js +32 -0
  11. package/dist/cjs/components/form/FormPlaceholder.js +43 -0
  12. package/dist/cjs/components/{authors/AuthorActions.js → form/ModalFormActions.js} +12 -11
  13. package/dist/cjs/plugins/affiliations.js +5 -38
  14. package/dist/cjs/useEditor.js +0 -1
  15. package/dist/cjs/versions.js +1 -1
  16. package/dist/cjs/views/affiliations.js +82 -14
  17. package/dist/cjs/views/contributors.js +1 -9
  18. package/dist/es/components/affiliations/AffiliationForm.js +76 -0
  19. package/dist/es/components/affiliations/AffiliationItem.js +82 -0
  20. package/dist/es/components/affiliations/AffiliationList.js +40 -0
  21. package/dist/es/components/affiliations/AffiliationsModal.js +388 -0
  22. package/dist/es/components/authors/AuthorDetailsForm.js +11 -3
  23. package/dist/es/components/authors/AuthorList.js +1 -1
  24. package/dist/es/components/authors/AuthorsModal.js +183 -79
  25. package/dist/es/components/authors/DraggableAuthor.js +0 -3
  26. package/dist/es/components/dialog/ConfirmationDialog.js +79 -0
  27. package/dist/es/components/form/FormFooter.js +27 -0
  28. package/dist/es/components/form/FormPlaceholder.js +36 -0
  29. package/dist/es/components/{authors/AuthorActions.js → form/ModalFormActions.js} +10 -9
  30. package/dist/es/plugins/affiliations.js +6 -39
  31. package/dist/es/useEditor.js +0 -1
  32. package/dist/es/versions.js +1 -1
  33. package/dist/es/views/affiliations.js +85 -17
  34. package/dist/es/views/contributors.js +1 -9
  35. package/dist/types/components/{authors → affiliations}/AffiliationForm.d.ts +5 -0
  36. package/dist/types/components/affiliations/AffiliationItem.d.ts +10 -0
  37. package/dist/types/components/affiliations/AffiliationList.d.ts +26 -0
  38. package/dist/types/components/affiliations/AffiliationsModal.d.ts +11 -0
  39. package/dist/types/components/authors/AuthorDetailsForm.d.ts +1 -0
  40. package/dist/types/components/authors/AuthorsModal.d.ts +2 -1
  41. package/dist/types/components/dialog/ConfirmationDialog.d.ts +15 -0
  42. package/dist/types/components/form/FormFooter.d.ts +5 -0
  43. package/dist/types/components/form/FormPlaceholder.d.ts +9 -0
  44. package/dist/types/components/{authors/AuthorActions.d.ts → form/ModalFormActions.d.ts} +4 -3
  45. package/dist/types/lib/dnd.d.ts +1 -1
  46. package/dist/types/selection.d.ts +7 -1
  47. package/dist/types/versions.d.ts +1 -1
  48. package/dist/types/views/affiliations.d.ts +11 -0
  49. package/dist/types/views/contributors.d.ts +0 -1
  50. package/package.json +2 -2
  51. package/styles/AdvancedEditor.css +8 -4
  52. package/styles/Editor.css +7 -0
  53. package/dist/cjs/components/authors/AffiliationButton.js +0 -104
  54. package/dist/cjs/components/authors/AffiliationForm.js +0 -50
  55. package/dist/cjs/components/authors/AuthorAffiliations.js +0 -120
  56. package/dist/cjs/components/authors/AuthorFormPlaceholder.js +0 -71
  57. package/dist/cjs/components/authors/DeleteAuthorConfirmationDialog.js +0 -36
  58. package/dist/cjs/components/authors/RequiredFieldConfirmationDialog.js +0 -39
  59. package/dist/cjs/components/authors/SaveAuthorConfirmationDialog.js +0 -39
  60. package/dist/es/components/authors/AffiliationButton.js +0 -74
  61. package/dist/es/components/authors/AffiliationForm.js +0 -43
  62. package/dist/es/components/authors/AuthorAffiliations.js +0 -89
  63. package/dist/es/components/authors/AuthorFormPlaceholder.js +0 -64
  64. package/dist/es/components/authors/DeleteAuthorConfirmationDialog.js +0 -29
  65. package/dist/es/components/authors/RequiredFieldConfirmationDialog.js +0 -32
  66. package/dist/es/components/authors/SaveAuthorConfirmationDialog.js +0 -32
  67. package/dist/types/components/authors/AffiliationButton.d.ts +0 -8
  68. package/dist/types/components/authors/AuthorAffiliations.d.ts +0 -11
  69. package/dist/types/components/authors/AuthorFormPlaceholder.d.ts +0 -2
  70. package/dist/types/components/authors/DeleteAuthorConfirmationDialog.d.ts +0 -7
  71. package/dist/types/components/authors/RequiredFieldConfirmationDialog.d.ts +0 -7
  72. package/dist/types/components/authors/SaveAuthorConfirmationDialog.d.ts +0 -7
@@ -14,31 +14,34 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { buildBibliographicName, generateID, ObjectTypes, } from '@manuscripts/json-schema';
17
- import { AddIcon, CloseButton, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, ScrollableModalContent, SidebarContent, StyledModal, } from '@manuscripts/style-guide';
17
+ import { AddIcon, AddInstitutionIcon, AuthorPlaceholderIcon, CloseButton, Drawer, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, ScrollableModalContent, SelectedItemsBox, SidebarContent, StyledModal, } from '@manuscripts/style-guide';
18
18
  import { isEqual, omit } from 'lodash';
19
19
  import React, { useEffect, useReducer, useRef, useState } from 'react';
20
20
  import styled from 'styled-components';
21
21
  import { arrayReducer } from '../../lib/array-reducer';
22
22
  import { authorComparator, } from '../../lib/authors';
23
- import { AuthorActions } from './AuthorActions';
24
- import { AuthorAffiliations } from './AuthorAffiliations';
23
+ import { ConfirmationDialog, DialogType } from '../dialog/ConfirmationDialog';
24
+ import FormFooter from '../form/FormFooter';
25
+ import { FormPlaceholder } from '../form/FormPlaceholder';
26
+ import { ModalFormActions } from '../form/ModalFormActions';
25
27
  import { AuthorDetailsForm } from './AuthorDetailsForm';
26
- import { AuthorFormPlaceholder } from './AuthorFormPlaceholder';
27
28
  import { AuthorList } from './AuthorList';
28
- import { RequiredFieldConfirmationDialog } from './RequiredFieldConfirmationDialog';
29
- import { SaveAuthorConfirmationDialog } from './SaveAuthorConfirmationDialog';
30
29
  const AddAuthorButton = styled.div `
31
30
  display: flex;
32
31
  align-items: center;
33
- margin-bottom: ${(props) => props.theme.grid.unit * 4}px;
34
- margin-left: ${(props) => props.theme.grid.unit * 4}px;
32
+ padding: 12px 8px 12px 12px;
35
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
+ }
36
40
  `;
37
41
  const ActionTitle = styled.div `
38
42
  padding-left: ${(props) => props.theme.grid.unit * 2}px;
39
43
  `;
40
44
  const FormLabel = styled.legend `
41
- margin-top: 12px;
42
45
  margin-bottom: 12px;
43
46
  font: ${(props) => props.theme.font.weight.normal}
44
47
  ${(props) => props.theme.font.size.xlarge} /
@@ -50,28 +53,73 @@ const FormLabel = styled.legend `
50
53
  const AuthorForms = styled.div `
51
54
  padding-left: ${(props) => props.theme.grid.unit * 3}px;
52
55
  padding-right: ${(props) => props.theme.grid.unit * 3}px;
56
+ position: relative;
57
+ margin-top: 20px;
53
58
  `;
54
59
  const StyledSidebarContent = styled(SidebarContent) `
55
60
  padding: 0;
56
61
  `;
57
- const authorsReducer = arrayReducer((a, b) => a.id === b.id);
58
- const affiliationsReducer = arrayReducer((a, b) => a.id === b.id);
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
+ `;
105
+ export const authorsReducer = arrayReducer((a, b) => a.id === b.id);
106
+ export const affiliationsReducer = arrayReducer((a, b) => a.id === b.id);
59
107
  const normalize = (author) => ({
60
108
  id: author.id,
61
- role: author.role,
62
- affiliations: author.affiliations || [],
109
+ role: author.role || 'author',
110
+ affiliations: (author.affiliations || []).sort(),
63
111
  bibliographicName: author.bibliographicName,
64
112
  email: author.email || '',
65
113
  isCorresponding: author.isCorresponding || false,
66
114
  ORCIDIdentifier: author.ORCIDIdentifier || '',
67
- priority: author.priority,
115
+ priority: author.priority || 0,
68
116
  isJointContributor: author.isJointContributor || false,
69
- userID: '',
70
- invitationID: '',
117
+ userID: author.userID || '',
118
+ invitationID: author.invitationID || '',
71
119
  footnote: author.footnote || [],
72
120
  corresp: author.corresp || [],
73
121
  });
74
- export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, author, onSaveAuthor, onDeleteAuthor, onSaveAffiliation, addNewAuthor = false, }) => {
122
+ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, author, onSaveAuthor, onDeleteAuthor, addNewAuthor = false, }) => {
75
123
  const [isOpen, setOpen] = useState(true);
76
124
  const [isDisableSave, setDisableSave] = useState(true);
77
125
  const [isEmailRequired, setEmailRequired] = useState(false);
@@ -84,38 +132,74 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
84
132
  const [nextAuthor, setNextAuthor] = useState(null);
85
133
  const [isSwitchingAuthor, setIsSwitchingAuthor] = useState(false);
86
134
  const [isCreatingNewAuthor, setIsCreatingNewAuthor] = useState(false);
135
+ const [showAffiliationDrawer, setShowAffiliationDrawer] = useState(false);
136
+ const [selectedAffiliations, setSelectedAffiliations] = useState([]);
87
137
  const valuesRef = useRef();
88
138
  const actionsRef = useRef();
89
139
  const [authors, dispatchAuthors] = useReducer(authorsReducer, $authors.sort(authorComparator));
90
- const [affiliations, dispatchAffiliations] = useReducer(affiliationsReducer, $affiliations);
140
+ const [affiliations] = useReducer(affiliationsReducer, $affiliations);
141
+ const affiliationItems = affiliations.map((affiliation) => ({
142
+ id: affiliation.id,
143
+ label: affiliation.institution,
144
+ country: affiliation.country,
145
+ city: affiliation.city,
146
+ state: affiliation.county,
147
+ }));
148
+ const [selectedAffiliationIds, setSelectedAffiliationIds] = useState([]);
91
149
  useEffect(() => {
92
150
  if (addNewAuthor) {
93
151
  handleAddAuthor();
94
152
  }
95
153
  }, [addNewAuthor]);
96
154
  const [selection, setSelection] = useState(author);
155
+ useEffect(() => {
156
+ const currentAuthor = selection;
157
+ const relevantAffiliations = affiliationItems.filter((item) => { var _a; return (_a = currentAuthor === null || currentAuthor === void 0 ? void 0 : currentAuthor.affiliations) === null || _a === void 0 ? void 0 : _a.includes(item.id); });
158
+ setSelectedAffiliations(relevantAffiliations);
159
+ setSelectedAffiliationIds(relevantAffiliations.map((item) => item.id));
160
+ }, []);
97
161
  const handleSelect = (author) => {
98
162
  const values = valuesRef.current;
99
163
  setIsCreatingNewAuthor(false);
100
- if (values && selection && unSavedChanges && !isDisableSave) {
101
- setShowConfirmationDialog(true);
102
- setNextAuthor(author);
103
- }
104
- else if (isDisableSave && unSavedChanges) {
105
- setShowRequiredFieldConfirmationDialog(true);
106
- setNextAuthor(author);
164
+ if (values && selection) {
165
+ const normalizedSelection = normalize(selection);
166
+ const normalizedValues = normalize(values);
167
+ const hasChanges = !isEqual(normalizedSelection, normalizedValues);
168
+ if (hasChanges && !isDisableSave) {
169
+ setShowConfirmationDialog(true);
170
+ setNextAuthor(author);
171
+ }
172
+ else if (hasChanges && isDisableSave) {
173
+ setShowRequiredFieldConfirmationDialog(true);
174
+ setNextAuthor(author);
175
+ }
176
+ else {
177
+ updateAffiliationSelection(author);
178
+ setSelection(author);
179
+ setShowAffiliationDrawer(false);
180
+ setNewAuthor(false);
181
+ }
107
182
  }
108
183
  else {
184
+ setShowAffiliationDrawer(false);
185
+ updateAffiliationSelection(author);
109
186
  setSelection(author);
110
187
  setNewAuthor(false);
111
188
  }
112
189
  };
190
+ const updateAffiliationSelection = (author) => {
191
+ const relevantAffiliations = affiliationItems.filter((item) => { var _a; return (_a = author.affiliations) === null || _a === void 0 ? void 0 : _a.includes(item.id); });
192
+ setSelectedAffiliations(relevantAffiliations);
193
+ setSelectedAffiliationIds(relevantAffiliations.map((item) => item.id));
194
+ };
113
195
  const handleClose = () => {
114
- if (isDisableSave && unSavedChanges) {
115
- setShowRequiredFieldConfirmationDialog(true);
116
- }
117
- else if (unSavedChanges) {
118
- setShowConfirmationDialog(true);
196
+ if (unSavedChanges) {
197
+ if (isDisableSave) {
198
+ setShowRequiredFieldConfirmationDialog(true);
199
+ }
200
+ else {
201
+ setShowConfirmationDialog(true);
202
+ }
119
203
  }
120
204
  else {
121
205
  setShowRequiredFieldConfirmationDialog(false);
@@ -131,6 +215,7 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
131
215
  setSelection(nextAuthor);
132
216
  setNextAuthor(null);
133
217
  setNewAuthor(false);
218
+ setShowAffiliationDrawer(false);
134
219
  setIsCreatingNewAuthor(false);
135
220
  }
136
221
  else if (isCreatingNewAuthor) {
@@ -142,10 +227,14 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
142
227
  const handleCancel = () => {
143
228
  handleResetAuthor();
144
229
  if (nextAuthor) {
230
+ const affiliations = nextAuthor.affiliations || [];
231
+ setSelectedAffiliationIds(affiliations);
232
+ setSelectedAffiliations(affiliationItems.filter((item) => affiliations.includes(item.id)));
145
233
  setSelection(nextAuthor);
146
234
  setNextAuthor(null);
147
235
  setNewAuthor(false);
148
236
  setIsCreatingNewAuthor(false);
237
+ setUnSavedChanges(false);
149
238
  }
150
239
  else if (newAuthor && unSavedChanges && !isSwitchingAuthor) {
151
240
  setNewAuthor(false);
@@ -157,6 +246,8 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
157
246
  setIsCreatingNewAuthor(false);
158
247
  }
159
248
  setShowConfirmationDialog(false);
249
+ setShowRequiredFieldConfirmationDialog(false);
250
+ setShowAffiliationDrawer(false);
160
251
  };
161
252
  const handleSaveAuthor = (values) => {
162
253
  if (!values || !selection) {
@@ -172,6 +263,9 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
172
263
  setSelection(author);
173
264
  setShowConfirmationDialog(false);
174
265
  setNewAuthor(false);
266
+ setSelectedAffiliationIds([]);
267
+ setSelectedAffiliations([]);
268
+ setShowAffiliationDrawer(false);
175
269
  setIsCreatingNewAuthor(false);
176
270
  dispatchAuthors({
177
271
  type: 'update',
@@ -211,6 +305,8 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
211
305
  footnote: [],
212
306
  };
213
307
  setIsSwitchingAuthor(!!selection);
308
+ setSelectedAffiliations([]);
309
+ setSelectedAffiliationIds([]);
214
310
  setSelection(author);
215
311
  setNewAuthor(true);
216
312
  };
@@ -218,7 +314,9 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
218
314
  const values = valuesRef.current;
219
315
  setIsSwitchingAuthor(!!selection);
220
316
  setIsCreatingNewAuthor(true);
221
- if (values && selection && !isEqual(values, normalize(selection))) {
317
+ if (values &&
318
+ selection &&
319
+ !isEqual(normalize(values), normalize(selection))) {
222
320
  if (isDisableSave) {
223
321
  setShowRequiredFieldConfirmationDialog(true);
224
322
  }
@@ -229,6 +327,7 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
229
327
  }
230
328
  else {
231
329
  createNewAuthor();
330
+ setShowAffiliationDrawer(false);
232
331
  }
233
332
  };
234
333
  const handleDeleteAuthor = () => {
@@ -237,37 +336,26 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
237
336
  }
238
337
  onDeleteAuthor(selection);
239
338
  setSelection(undefined);
339
+ setUnSavedChanges(false);
240
340
  dispatchAuthors({
241
341
  type: 'delete',
242
342
  item: selection,
243
343
  });
244
344
  };
245
- const handleSaveAffiliation = (affiliation) => {
246
- onSaveAffiliation(affiliation);
247
- dispatchAffiliations({
248
- type: 'update',
249
- items: [affiliation],
250
- });
251
- };
252
- const handleAddAffiliation = (affiliation) => {
253
- if (!valuesRef.current) {
254
- return;
255
- }
256
- const values = valuesRef.current;
257
- const affiliations = values.affiliations || [];
258
- handleSaveAuthor(Object.assign(Object.assign({}, values), { affiliations: [...affiliations, affiliation.id] }));
259
- };
260
- const handleRemoveAffiliation = (affiliation) => {
261
- var _a;
262
- if (!valuesRef.current) {
345
+ const handleRemoveAffiliation = (affId) => {
346
+ if (!selection) {
263
347
  return;
264
348
  }
265
- const values = valuesRef.current;
266
- handleSaveAuthor(Object.assign(Object.assign({}, values), { affiliations: (_a = values.affiliations) === null || _a === void 0 ? void 0 : _a.filter((i) => i !== affiliation.id) }));
349
+ const newAffiliations = selectedAffiliationIds.filter((id) => id !== affId);
350
+ setSelectedAffiliationIds(newAffiliations);
351
+ setSelectedAffiliations(affiliationItems.filter((item) => newAffiliations.includes(item.id)));
267
352
  };
268
353
  const handleResetAuthor = () => {
269
354
  var _a;
270
355
  (_a = actionsRef.current) === null || _a === void 0 ? void 0 : _a.reset();
356
+ const affiliations = (selection === null || selection === void 0 ? void 0 : selection.affiliations) || [];
357
+ setSelectedAffiliationIds(affiliations);
358
+ setSelectedAffiliations(affiliationItems.filter((item) => affiliations.includes(item.id)));
271
359
  setShowConfirmationDialog(false);
272
360
  setShowRequiredFieldConfirmationDialog(false);
273
361
  setUnSavedChanges(false);
@@ -282,60 +370,76 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
282
370
  };
283
371
  const handleChangeAuthor = (values) => {
284
372
  const normalized = omit(normalize(selection), 'priority');
285
- const updatedValues = omit(values, 'priority');
286
- if (updatedValues.id === normalized.id &&
287
- !isEqual(updatedValues, normalized)) {
373
+ const updatedValues = omit(normalize(values), 'priority');
374
+ const isSameAuthor = updatedValues.id === normalized.id;
375
+ const hasChanges = !isEqual(updatedValues, normalized);
376
+ if (isSameAuthor && hasChanges) {
288
377
  setUnSavedChanges(true);
289
378
  }
290
- const isUnchanged = isEqual(normalize(selection), values);
291
- valuesRef.current = values;
379
+ else {
380
+ setUnSavedChanges(false);
381
+ }
382
+ valuesRef.current = Object.assign(Object.assign({}, updatedValues), { priority: values.priority });
292
383
  const { given, family } = values.bibliographicName;
293
384
  const { email, isCorresponding } = values;
294
385
  const isNameFilled = (given === null || given === void 0 ? void 0 : given.length) && (family === null || family === void 0 ? void 0 : family.length);
295
- if (isNameFilled && !isCorresponding && !isUnchanged) {
296
- setDisableSave(false);
297
- }
298
- else if (isCorresponding &&
299
- (email === null || email === void 0 ? void 0 : email.length) &&
300
- isNameFilled &&
301
- !isUnchanged) {
302
- setDisableSave(false);
386
+ if (hasChanges && isNameFilled) {
387
+ if (isCorresponding) {
388
+ setDisableSave(!(email === null || email === void 0 ? void 0 : email.length));
389
+ }
390
+ else {
391
+ setDisableSave(false);
392
+ }
303
393
  }
304
394
  else {
305
395
  setDisableSave(true);
306
396
  }
307
- if (isCorresponding && !(email === null || email === void 0 ? void 0 : email.length)) {
308
- setEmailRequired(true);
309
- }
310
- else {
311
- setEmailRequired(false);
312
- }
397
+ setEmailRequired(isCorresponding);
313
398
  };
314
399
  const handleShowDeleteDialog = () => {
315
400
  setShowDeleteDialog((prev) => !prev);
316
401
  };
402
+ const handleAffiliationSelect = (affiliationId) => {
403
+ if (!selection) {
404
+ return;
405
+ }
406
+ const currentAffiliations = selectedAffiliationIds || [];
407
+ const isAlreadySelected = currentAffiliations.includes(affiliationId);
408
+ const newAffiliations = isAlreadySelected
409
+ ? currentAffiliations.filter((id) => id !== affiliationId)
410
+ : [...currentAffiliations, affiliationId];
411
+ setSelectedAffiliationIds(newAffiliations);
412
+ setSelectedAffiliations(affiliationItems.filter((item) => newAffiliations.includes(item.id)));
413
+ };
317
414
  return (React.createElement(StyledModal, { isOpen: isOpen, onRequestClose: () => handleClose(), shouldCloseOnOverlayClick: true },
318
415
  React.createElement(ModalContainer, null,
319
416
  React.createElement(ModalHeader, null,
320
417
  React.createElement(CloseButton, { onClick: () => handleClose(), "data-cy": "modal-close-button" })),
321
- React.createElement(ModalBody, null,
418
+ React.createElement(StyledModalBody, null,
322
419
  React.createElement(ModalSidebar, { "data-cy": "authors-sidebar" },
323
- React.createElement(ModalSidebarHeader, null,
420
+ React.createElement(StyledModalSidebarHeader, null,
324
421
  React.createElement(ModalSidebarTitle, null, "Authors")),
325
422
  React.createElement(StyledSidebarContent, null,
326
- React.createElement(AddAuthorButton, { "data-cy": "add-author-button", onClick: handleAddAuthor },
423
+ React.createElement(AddAuthorButton, { "data-cy": "add-author-button", onClick: handleAddAuthor, "data-active": isCreatingNewAuthor },
327
424
  React.createElement(AddIcon, { width: 18, height: 18 }),
328
425
  React.createElement(ActionTitle, null, "New Author")),
329
426
  React.createElement(AuthorList, { author: selection, authors: authors, onSelect: handleSelect, onDelete: handleShowDeleteDialog, moveAuthor: handleMoveAuthor, lastSavedAuthor: lastSavedAuthor }))),
330
427
  React.createElement(ScrollableModalContent, { "data-cy": "author-modal-content" }, selection ? (React.createElement(AuthorForms, null,
331
- React.createElement(RequiredFieldConfirmationDialog, { isOpen: showRequiredFieldConfirmationDialog, onSave: () => setShowRequiredFieldConfirmationDialog(false), onCancel: handleCancel }),
332
- React.createElement(SaveAuthorConfirmationDialog, { isOpen: showConfirmationDialog, onSave: handleSave, onCancel: handleCancel }),
333
- React.createElement(AuthorActions, { onSave: () => handleSaveAuthor(valuesRef.current), onDelete: handleDeleteAuthor, showDeleteDialog: showDeleteDialog, handleShowDeleteDialog: handleShowDeleteDialog, newAuthor: newAuthor ||
428
+ React.createElement(ConfirmationDialog, { isOpen: showRequiredFieldConfirmationDialog, onPrimary: () => setShowRequiredFieldConfirmationDialog(false), onSecondary: handleCancel, type: DialogType.REQUIRED, entityType: "author" }),
429
+ React.createElement(ConfirmationDialog, { isOpen: showConfirmationDialog, onPrimary: handleSave, onSecondary: handleCancel, type: DialogType.SAVE, entityType: "author" }),
430
+ React.createElement(ModalFormActions, { type: "author", onSave: () => handleSaveAuthor(valuesRef.current), onDelete: handleDeleteAuthor, showDeleteDialog: showDeleteDialog, handleShowDeleteDialog: handleShowDeleteDialog, newEntity: newAuthor ||
334
431
  (isCreatingNewAuthor &&
335
432
  !showConfirmationDialog &&
336
433
  !showRequiredFieldConfirmationDialog), isDisableSave: isDisableSave }),
337
434
  React.createElement(FormLabel, null, "Details"),
338
- React.createElement(AuthorDetailsForm, { values: normalize(selection), onChange: handleChangeAuthor, onSave: handleSaveAuthor, actionsRef: actionsRef, isEmailRequired: isEmailRequired }),
339
- React.createElement(FormLabel, null, "Affiliations"),
340
- React.createElement(AuthorAffiliations, { author: selection, affiliations: affiliations, onSave: handleSaveAffiliation, onAdd: handleAddAffiliation, onRemove: handleRemoveAffiliation }))) : (React.createElement(AuthorFormPlaceholder, null)))))));
435
+ React.createElement(AuthorDetailsForm, { values: normalize(selection), onChange: handleChangeAuthor, onSave: handleSaveAuthor, actionsRef: actionsRef, isEmailRequired: isEmailRequired, selectedAffiliations: selectedAffiliationIds }),
436
+ React.createElement(AuthorsSection, null,
437
+ React.createElement(AuthorsHeader, null,
438
+ React.createElement(AuthorsTitle, null, "Authors"),
439
+ React.createElement(AffiliateButton, { onClick: () => setShowAffiliationDrawer(true), "data-cy": "affiliate-authors-button" },
440
+ React.createElement(AddInstitutionIcon, { width: 16, height: 16 }),
441
+ "Assign Institutions")),
442
+ React.createElement(SelectedItemsBox, { items: selectedAffiliations, onRemove: handleRemoveAffiliation, placeholder: "No institutions assigned" })),
443
+ 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) })))),
444
+ React.createElement(FormFooter, { onCancel: handleClose }))));
341
445
  };
@@ -33,9 +33,6 @@ const AuthorContainer = styled.div `
33
33
  &:hover,
34
34
  &.active {
35
35
  background: ${(props) => props.theme.colors.background.fifth};
36
- }
37
-
38
- &.active {
39
36
  border-color: ${(props) => props.theme.colors.border.primary};
40
37
  }
41
38
 
@@ -0,0 +1,79 @@
1
+ import { AttentionOrangeIcon, Category, Dialog } from '@manuscripts/style-guide';
2
+ import React from 'react';
3
+ import styled from 'styled-components';
4
+ const StyledIcon = styled(AttentionOrangeIcon) `
5
+ margin-right: 8px;
6
+ `;
7
+ const MessageBox = styled.div `
8
+ display: flex;
9
+ flex-direction: column;
10
+ justify-content: space-between;
11
+ min-height: 90px;
12
+ `;
13
+ export var DialogType;
14
+ (function (DialogType) {
15
+ DialogType["SAVE"] = "save";
16
+ DialogType["DELETE"] = "delete";
17
+ DialogType["REQUIRED"] = "required";
18
+ })(DialogType || (DialogType = {}));
19
+ const getDialogConfig = (dialogType, entityType) => {
20
+ const configs = {
21
+ [DialogType.SAVE]: {
22
+ title: 'Unsaved changes',
23
+ message: {
24
+ primary: "You've made changes but not saved them!",
25
+ secondary: 'Would you like to save or discard your changes?',
26
+ },
27
+ buttons: {
28
+ primary: 'Save',
29
+ secondary: 'Discard',
30
+ },
31
+ },
32
+ [DialogType.DELETE]: {
33
+ title: `Delete ${entityType}`,
34
+ message: {
35
+ primary: entityType === 'affiliation'
36
+ ? 'This action will delete the institution and any links to authors affiliated to it.'
37
+ : `Are you sure you want to remove this ${entityType} from the list?`,
38
+ secondary: 'Do you want to continue?',
39
+ },
40
+ buttons: {
41
+ primary: 'Delete',
42
+ secondary: 'Cancel',
43
+ },
44
+ },
45
+ [DialogType.REQUIRED]: {
46
+ title: 'Empty required field',
47
+ message: {
48
+ primary: 'A required field has been left empty. If you discard, any updates will not be saved!',
49
+ secondary: 'Would you like to discard or continue editing?',
50
+ },
51
+ buttons: {
52
+ primary: 'Continue editing',
53
+ secondary: 'Discard',
54
+ },
55
+ minHeight: '90px',
56
+ },
57
+ };
58
+ return configs[dialogType];
59
+ };
60
+ export const ConfirmationDialog = ({ isOpen, type, entityType, onPrimary, onSecondary, }) => {
61
+ const config = getDialogConfig(type, entityType);
62
+ const Header = () => (React.createElement(React.Fragment, null,
63
+ React.createElement(StyledIcon, null),
64
+ " ",
65
+ config.title));
66
+ const Message = () => (React.createElement(MessageBox, null,
67
+ React.createElement("div", null, config.message.primary),
68
+ config.message.secondary && React.createElement("div", null, config.message.secondary)));
69
+ return (React.createElement(Dialog, { isOpen: isOpen, category: Category.confirmation, header: React.createElement(Header, null), message: React.createElement(Message, null), actions: {
70
+ primary: {
71
+ action: onPrimary,
72
+ title: config.buttons.primary,
73
+ },
74
+ secondary: {
75
+ action: onSecondary,
76
+ title: config.buttons.secondary,
77
+ },
78
+ } }));
79
+ };
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ const Footer = styled.div `
4
+ display: flex;
5
+ justify-content: flex-end;
6
+ align-items: center;
7
+ padding: 16px 32px;
8
+ height: 40px;
9
+ box-shadow: 0px -2px 12px 0px rgba(216, 216, 216, 0.26);
10
+ border-radius: 0px 0px 8px 8px;
11
+ `;
12
+ const RemoveButton = styled.button `
13
+ background-color: #0d79d0;
14
+ color: ${(props) => props.theme.colors.text.onDark};
15
+ padding: 8px 32px;
16
+ border-radius: 4px;
17
+ border: 1px solid #0d79d0;
18
+ &:hover {
19
+ background-color: ${(props) => props.theme.colors.background.hover};
20
+ }
21
+ cursor: pointer;
22
+ `;
23
+ const FormFooter = ({ onCancel }) => {
24
+ return (React.createElement(Footer, null,
25
+ React.createElement(RemoveButton, { onClick: onCancel }, "Close")));
26
+ };
27
+ export default FormFooter;
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ const Container = styled.div `
4
+ display: flex;
5
+ flex-direction: column;
6
+ align-items: center;
7
+ justify-content: center;
8
+ height: 100%;
9
+ overflow-y: auto;
10
+ `;
11
+ const Placeholder = styled.div `
12
+ margin-bottom: ${(props) => props.theme.grid.unit * 5}px;
13
+ `;
14
+ const Action = styled.div `
15
+ font-size: ${(props) => props.theme.font.size.xlarge};
16
+ font-weight: ${(props) => props.theme.font.weight.medium};
17
+ letter-spacing: -0.5px;
18
+ `;
19
+ const Message = styled.div `
20
+ max-width: 400px;
21
+ font-size: ${(props) => props.theme.font.size.xlarge};
22
+ margin-top: ${(props) => props.theme.grid.unit * 6}px;
23
+ font-weight: ${(props) => props.theme.font.weight.light};
24
+ color: ${(props) => props.theme.colors.text.secondary};
25
+ text-align: center;
26
+
27
+ @media (max-width: 850px) {
28
+ margin-right: ${(props) => props.theme.grid.unit * 5}px;
29
+ margin-left: ${(props) => props.theme.grid.unit * 5}px;
30
+ max-width: 350px;
31
+ }
32
+ `;
33
+ export const FormPlaceholder = ({ type, title, message, placeholderIcon, }) => (React.createElement(Container, { "data-cy": `${type}-details` },
34
+ React.createElement(Placeholder, null, placeholderIcon),
35
+ React.createElement(Action, null, title),
36
+ React.createElement(Message, null, message)));
@@ -1,8 +1,8 @@
1
1
  import { ButtonGroup, IconButton, PlusIcon } from '@manuscripts/style-guide';
2
2
  import React from 'react';
3
3
  import styled from 'styled-components';
4
- import { DeleteAuthorConfirmationDialog } from './DeleteAuthorConfirmationDialog';
5
- const AuthorActionsContainer = styled.div `
4
+ import { ConfirmationDialog, DialogType } from '../dialog/ConfirmationDialog';
5
+ const ActionsContainer = styled.div `
6
6
  display: flex;
7
7
  `;
8
8
  const StyledButtonGroup = styled(ButtonGroup) `
@@ -14,8 +14,9 @@ const StyledIconButton = styled(IconButton) `
14
14
  font-size: 14px;
15
15
  font-style: normal;
16
16
  font-weight: 400;
17
- line-height: 24px;
17
+ line-height: 1;
18
18
  width: auto;
19
+ height: 24px;
19
20
  &:disabled {
20
21
  color: #c9c9c9 !important;
21
22
  background-color: unset !important;
@@ -25,14 +26,14 @@ const StyledIconButton = styled(IconButton) `
25
26
  margin-right: 4px;
26
27
  }
27
28
  `;
28
- export const AuthorActions = ({ onSave, onDelete, showDeleteDialog, handleShowDeleteDialog, newAuthor, isDisableSave, }) => {
29
- return (React.createElement(AuthorActionsContainer, { "data-cy": "author-action" },
30
- React.createElement(DeleteAuthorConfirmationDialog, { isOpen: showDeleteDialog, onDelete: () => {
31
- handleShowDeleteDialog();
29
+ export const ModalFormActions = ({ type, onSave, onDelete, showDeleteDialog, handleShowDeleteDialog, newEntity, isDisableSave, }) => {
30
+ return (React.createElement(ActionsContainer, { "data-cy": `${type}-action` },
31
+ React.createElement(ConfirmationDialog, { isOpen: showDeleteDialog, onPrimary: () => {
32
32
  onDelete();
33
- }, onCancel: () => handleShowDeleteDialog() }),
33
+ handleShowDeleteDialog();
34
+ }, onSecondary: handleShowDeleteDialog, type: DialogType.DELETE, entityType: type }),
34
35
  React.createElement(StyledButtonGroup, null,
35
36
  React.createElement(StyledIconButton, { onClick: onSave, disabled: isDisableSave, type: "submit" },
36
37
  React.createElement(PlusIcon, null),
37
- newAuthor ? 'Save Details' : 'Update Details'))));
38
+ newEntity ? 'Save Details' : 'Update Details'))));
38
39
  };