@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.
Files changed (39) hide show
  1. package/dist/cjs/components/affiliations/AffiliationsModal.js +79 -126
  2. package/dist/cjs/components/authors/AffiliationDrawer.js +24 -0
  3. package/dist/cjs/components/authors/AuthorDetailsForm.js +11 -9
  4. package/dist/cjs/components/authors/AuthorsModal.js +121 -204
  5. package/dist/cjs/components/authors/CreditDrawer.js +37 -0
  6. package/dist/cjs/components/authors/useManageAffiliations.js +41 -0
  7. package/dist/cjs/components/authors/useManageCredit.js +43 -0
  8. package/dist/cjs/components/form/FormFooter.js +2 -0
  9. package/dist/cjs/components/form/ModalFormActions.js +4 -4
  10. package/dist/cjs/components/modal-drawer/GenericDrawer.js +16 -0
  11. package/dist/cjs/components/modal-drawer/GenericDrawerGroup.js +75 -0
  12. package/dist/cjs/lib/normalize.js +49 -0
  13. package/dist/cjs/versions.js +1 -1
  14. package/dist/es/components/affiliations/AffiliationsModal.js +80 -127
  15. package/dist/es/components/authors/AffiliationDrawer.js +17 -0
  16. package/dist/es/components/authors/AuthorDetailsForm.js +9 -7
  17. package/dist/es/components/authors/AuthorsModal.js +123 -206
  18. package/dist/es/components/authors/CreditDrawer.js +30 -0
  19. package/dist/es/components/authors/useManageAffiliations.js +37 -0
  20. package/dist/es/components/authors/useManageCredit.js +39 -0
  21. package/dist/es/components/form/FormFooter.js +2 -0
  22. package/dist/es/components/form/ModalFormActions.js +4 -4
  23. package/dist/es/components/modal-drawer/GenericDrawer.js +9 -0
  24. package/dist/es/components/modal-drawer/GenericDrawerGroup.js +68 -0
  25. package/dist/es/lib/normalize.js +44 -0
  26. package/dist/es/versions.js +1 -1
  27. package/dist/types/components/authors/AffiliationDrawer.d.ts +12 -0
  28. package/dist/types/components/authors/AuthorDetailsForm.d.ts +4 -1
  29. package/dist/types/components/authors/AuthorsModal.d.ts +1 -2
  30. package/dist/types/components/authors/CreditDrawer.d.ts +14 -0
  31. package/dist/types/components/authors/useManageAffiliations.d.ts +18 -0
  32. package/dist/types/components/authors/useManageCredit.d.ts +13 -0
  33. package/dist/types/components/form/ModalFormActions.d.ts +2 -2
  34. package/dist/types/components/modal-drawer/GenericDrawer.d.ts +14 -0
  35. package/dist/types/components/modal-drawer/GenericDrawerGroup.d.ts +45 -0
  36. package/dist/types/lib/normalize.d.ts +25 -0
  37. package/dist/types/types.d.ts +1 -0
  38. package/dist/types/versions.d.ts +1 -1
  39. package/package.json +4 -4
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * © 2024 Atypon Systems LLC
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, generateID, ObjectTypes, } from '@manuscripts/json-schema';
17
- import { AddIcon, AddInstitutionIcon, AuthorPlaceholderIcon, CloseButton, Drawer, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, ScrollableModalContent, SelectedItemsBox, SidebarContent, StyledModal, } from '@manuscripts/style-guide';
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
- const AddAuthorButton = styled.div `
30
- display: flex;
31
- align-items: center;
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 [showDeleteDialog, setShowDeleteDialog] = useState(false);
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 [showAffiliationDrawer, setShowAffiliationDrawer] = useState(false);
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
- handleAddAuthor();
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 = affiliationItems.filter((item) => currentAuthor?.affiliations?.includes(item.id));
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 handleSelect = (author) => {
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 = normalize(selection);
171
- const normalizedValues = normalize(values);
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 = affiliationItems.filter((item) => author.affiliations?.includes(item.id));
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 handleClose = () => {
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 handleSave = () => {
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
- handleSaveAuthor(valuesRef.current);
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 handleCancel = () => {
242
- handleResetAuthor();
144
+ const cancel = () => {
145
+ resetAuthor();
243
146
  if (nextAuthor) {
244
- const affiliations = nextAuthor.affiliations || [];
245
- setSelectedAffiliationIds(affiliations);
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 handleSaveAuthor = (values) => {
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 handleMoveAuthor = (from, to, shift) => {
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
- setSelectedAffiliationIds([]);
222
+ setSelectedCreditRoles([]);
336
223
  setSelection(author);
337
224
  setNewAuthor(true);
338
225
  };
339
- const handleAddAuthor = () => {
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(normalize(values), normalize(selection))) {
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 handleDeleteAuthor = () => {
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 handleRemoveAffiliation = (affId) => {
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 affiliations = selection?.affiliations || [];
382
- setSelectedAffiliationIds(affiliations);
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 handleChangeAuthor = (values) => {
397
- const normalized = omit(normalize(selection), 'priority');
398
- const updatedValues = omit(normalize(values), 'priority');
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
- if (isSameAuthor && hasChanges) {
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 handleShowDeleteDialog = () => {
425
- setShowDeleteDialog((prev) => !prev);
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: () => handleClose(), "data-cy": "modal-close-button" })),
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: handleAddAuthor, "data-active": isCreatingNewAuthor || newAuthor },
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: handleSelect, onDelete: handleShowDeleteDialog, moveAuthor: handleMoveAuthor, lastSavedAuthor: lastSavedAuthor }))),
452
- React.createElement(ScrollableModalContent, { "data-cy": "author-modal-content" }, selection ? (React.createElement(AuthorForms, null,
453
- React.createElement(ConfirmationDialog, { isOpen: showRequiredFieldConfirmationDialog, onPrimary: () => setShowRequiredFieldConfirmationDialog(false), onSecondary: handleCancel, type: DialogType.REQUIRED, entityType: "author" }),
454
- React.createElement(ConfirmationDialog, { isOpen: showConfirmationDialog, onPrimary: handleSave, onSecondary: handleCancel, type: DialogType.SAVE, entityType: "author" }),
455
- React.createElement(ModalFormActions, { form: 'author-details-form', type: "author", onDelete: handleDeleteAuthor, showDeleteDialog: showDeleteDialog, handleShowDeleteDialog: handleShowDeleteDialog, newEntity: newAuthor ||
456
- (isCreatingNewAuthor &&
457
- !showConfirmationDialog &&
458
- !showRequiredFieldConfirmationDialog), isDisableSave: isDisableSave }),
459
- React.createElement(FormLabel, null, "Details"),
460
- React.createElement(AuthorDetailsForm, { values: normalize(selection), onChange: handleChangeAuthor, onSave: handleSaveAuthor, actionsRef: actionsRef, isEmailRequired: isEmailRequired, selectedAffiliations: selectedAffiliationIds, authorFormRef: authorFormRef }),
461
- React.createElement(AuthorsSection, null,
462
- React.createElement(AuthorsHeader, null,
463
- React.createElement(AuthorsTitle, null, "Authors"),
464
- React.createElement(AffiliateButton, { onClick: () => setShowAffiliationDrawer(true), "data-cy": "affiliate-authors-button" },
465
- React.createElement(AddInstitutionIcon, { width: 16, height: 16 }),
466
- "Assign Institutions")),
467
- React.createElement(SelectedItemsBox, { "data-cy": "author-affiliations", items: selectedAffiliations, onRemove: handleRemoveAffiliation, placeholder: "No institutions assigned" })),
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
+ };
@@ -8,6 +8,8 @@ const Footer = styled.div `
8
8
  height: 40px;
9
9
  box-shadow: 0px -2px 12px 0px rgba(216, 216, 216, 0.26);
10
10
  border-radius: 0px 0px 8px 8px;
11
+ position: relative;
12
+ z-index: 3;
11
13
  `;
12
14
  const RemoveButton = styled.button `
13
15
  background-color: #0d79d0;
@@ -26,12 +26,12 @@ const StyledIconButton = styled(IconButton) `
26
26
  margin-right: 4px;
27
27
  }
28
28
  `;
29
- export const ModalFormActions = ({ type, form, onDelete, showDeleteDialog, handleShowDeleteDialog, newEntity, isDisableSave, }) => {
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: showDeleteDialog, onPrimary: () => {
31
+ React.createElement(ConfirmationDialog, { isOpen: showingDeleteDialog, onPrimary: () => {
32
32
  onDelete();
33
- handleShowDeleteDialog();
34
- }, onSecondary: handleShowDeleteDialog, type: DialogType.DELETE, entityType: type }),
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
+ };