@manuscripts/body-editor 2.6.37 → 2.6.38-LEAN-4186.2

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/commands.js +26 -4
  2. package/dist/cjs/components/authors/AuthorActions.js +25 -41
  3. package/dist/cjs/components/authors/AuthorDetailsForm.js +30 -11
  4. package/dist/cjs/components/authors/AuthorList.js +11 -2
  5. package/dist/cjs/components/authors/AuthorsModal.js +122 -17
  6. package/dist/cjs/components/authors/DeleteAuthorConfirmationDialog.js +17 -4
  7. package/dist/cjs/components/authors/DraggableAuthor.js +62 -15
  8. package/dist/cjs/components/authors/RequiredFieldConfirmationDialog.js +39 -0
  9. package/dist/cjs/components/authors/SaveAuthorConfirmationDialog.js +19 -1
  10. package/dist/cjs/menus.js +7 -1
  11. package/dist/cjs/plugins/cross-references.js +1 -1
  12. package/dist/cjs/versions.js +1 -1
  13. package/dist/cjs/views/contributors.js +42 -15
  14. package/dist/es/commands.js +24 -3
  15. package/dist/es/components/authors/AuthorActions.js +26 -19
  16. package/dist/es/components/authors/AuthorDetailsForm.js +30 -11
  17. package/dist/es/components/authors/AuthorList.js +11 -2
  18. package/dist/es/components/authors/AuthorsModal.js +123 -18
  19. package/dist/es/components/authors/DeleteAuthorConfirmationDialog.js +18 -5
  20. package/dist/es/components/authors/DraggableAuthor.js +64 -16
  21. package/dist/es/components/authors/RequiredFieldConfirmationDialog.js +32 -0
  22. package/dist/es/components/authors/SaveAuthorConfirmationDialog.js +20 -2
  23. package/dist/es/menus.js +8 -2
  24. package/dist/es/plugins/cross-references.js +1 -1
  25. package/dist/es/versions.js +1 -1
  26. package/dist/es/views/contributors.js +43 -16
  27. package/dist/types/commands.d.ts +1 -0
  28. package/dist/types/components/authors/AuthorActions.d.ts +4 -3
  29. package/dist/types/components/authors/AuthorDetailsForm.d.ts +1 -0
  30. package/dist/types/components/authors/AuthorList.d.ts +2 -0
  31. package/dist/types/components/authors/AuthorsModal.d.ts +1 -0
  32. package/dist/types/components/authors/DeleteAuthorConfirmationDialog.d.ts +0 -2
  33. package/dist/types/components/authors/DraggableAuthor.d.ts +2 -0
  34. package/dist/types/components/authors/RequiredFieldConfirmationDialog.d.ts +7 -0
  35. package/dist/types/versions.d.ts +1 -1
  36. package/dist/types/views/contributors.d.ts +3 -2
  37. package/package.json +2 -2
  38. package/styles/AdvancedEditor.css +31 -2
  39. package/styles/Editor.css +15 -0
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.autoComplete = exports.mergeCellsWithSpace = exports.addColumns = exports.addHeaderRow = exports.addRows = exports.addInlineComment = exports.addNodeComment = exports.createAndFillTableElement = exports.selectAllIsolating = exports.ignoreAtomBlockNodeForward = exports.isAtEndOfTextBlock = exports.ignoreMetaNodeBackspaceCommand = exports.ignoreAtomBlockNodeBackward = exports.isTextSelection = exports.isAtStartOfTextBlock = exports.insertTOCSection = exports.insertBibliographySection = exports.insertList = exports.insertKeywords = exports.insertContributors = exports.insertAbstract = exports.insertBackmatterSection = exports.insertSection = exports.insertGraphicalAbstract = exports.insertBoxElement = exports.insertInlineFootnote = exports.insertFootnotesElement = exports.insertTableElementFooter = exports.insertInlineEquation = exports.insertCrossReference = exports.insertInlineCitation = exports.insertLink = exports.insertSectionLabel = exports.findPosBeforeFirstSubsection = exports.insertBreak = exports.deleteBlock = exports.insertBlock = exports.insertSupplement = exports.insertTable = exports.insertFigure = exports.insertGeneralTableFootnote = exports.insertInlineTableFootnote = exports.createBlock = exports.createSelection = exports.canInsert = exports.blockActive = exports.isNodeSelection = exports.markActive = exports.addToStart = void 0;
18
+ exports.autoComplete = exports.mergeCellsWithSpace = exports.addColumns = exports.addHeaderRow = exports.addRows = exports.addInlineComment = exports.addNodeComment = exports.createAndFillTableElement = exports.selectAllIsolating = exports.ignoreAtomBlockNodeForward = exports.isAtEndOfTextBlock = exports.ignoreMetaNodeBackspaceCommand = exports.ignoreAtomBlockNodeBackward = exports.isTextSelection = exports.isAtStartOfTextBlock = exports.insertTOCSection = exports.insertBibliographySection = exports.insertList = exports.insertKeywords = exports.insertAffiliation = exports.insertContributors = exports.insertAbstract = exports.insertBackmatterSection = exports.insertSection = exports.insertGraphicalAbstract = exports.insertBoxElement = exports.insertInlineFootnote = exports.insertFootnotesElement = exports.insertTableElementFooter = exports.insertInlineEquation = exports.insertCrossReference = exports.insertInlineCitation = exports.insertLink = exports.insertSectionLabel = exports.findPosBeforeFirstSubsection = exports.insertBreak = exports.deleteBlock = exports.insertBlock = exports.insertSupplement = exports.insertTable = exports.insertFigure = exports.insertGeneralTableFootnote = exports.insertInlineTableFootnote = exports.createBlock = exports.createSelection = exports.canInsert = exports.blockActive = exports.isNodeSelection = exports.markActive = exports.addToStart = void 0;
19
19
  const json_schema_1 = require("@manuscripts/json-schema");
20
20
  const track_changes_plugin_1 = require("@manuscripts/track-changes-plugin");
21
21
  const transform_1 = require("@manuscripts/transform");
@@ -619,9 +619,7 @@ const insertContributors = (state, dispatch, view) => {
619
619
  const contributors = state.schema.nodes.contributors.create({
620
620
  id: '',
621
621
  });
622
- const affiliations = state.schema.nodes.affiliations.create({ id: '' });
623
- const fragment = prosemirror_model_1.Fragment.fromArray([contributors, affiliations]);
624
- const tr = state.tr.insert(pos, fragment);
622
+ const tr = state.tr.insert(pos, contributors);
625
623
  if (dispatch) {
626
624
  const selection = prosemirror_state_1.NodeSelection.create(tr.doc, pos);
627
625
  if (view) {
@@ -632,6 +630,30 @@ const insertContributors = (state, dispatch, view) => {
632
630
  return true;
633
631
  };
634
632
  exports.insertContributors = insertContributors;
633
+ const insertAffiliation = (state, dispatch, view) => {
634
+ if ((0, utils_1.getChildOfType)(state.doc, transform_1.schema.nodes.affiliations, true)) {
635
+ return false;
636
+ }
637
+ const title = (0, prosemirror_utils_1.findChildrenByType)(state.doc, state.schema.nodes.title)[0];
638
+ let pos = title.pos + title.node.nodeSize;
639
+ const contributors = (0, prosemirror_utils_1.findChildrenByType)(state.doc, state.schema.nodes.contributors)[0];
640
+ if (contributors) {
641
+ pos = contributors.pos + contributors.node.nodeSize;
642
+ }
643
+ const affiliations = state.schema.nodes.affiliations.create({
644
+ id: '',
645
+ });
646
+ const tr = state.tr.insert(pos, affiliations);
647
+ if (dispatch) {
648
+ const selection = prosemirror_state_1.NodeSelection.create(tr.doc, pos);
649
+ if (view) {
650
+ view.focus();
651
+ }
652
+ dispatch(tr.setSelection(selection).scrollIntoView());
653
+ }
654
+ return true;
655
+ };
656
+ exports.insertAffiliation = insertAffiliation;
635
657
  const insertKeywords = (state, dispatch, view) => {
636
658
  if ((0, utils_1.getChildOfType)(state.doc, transform_1.schema.nodes.keywords, true)) {
637
659
  return false;
@@ -1,61 +1,45 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
4
  };
28
5
  Object.defineProperty(exports, "__esModule", { value: true });
29
6
  exports.AuthorActions = void 0;
30
7
  const style_guide_1 = require("@manuscripts/style-guide");
31
- const react_1 = __importStar(require("react"));
8
+ const react_1 = __importDefault(require("react"));
32
9
  const styled_components_1 = __importDefault(require("styled-components"));
33
10
  const DeleteAuthorConfirmationDialog_1 = require("./DeleteAuthorConfirmationDialog");
34
11
  const AuthorActionsContainer = styled_components_1.default.div `
35
12
  display: flex;
36
13
  `;
37
- const RemoveButton = styled_components_1.default.div `
38
- display: flex;
39
- align-items: center;
40
-
41
- svg {
42
- cursor: pointer;
43
- }
44
- `;
45
14
  const StyledButtonGroup = (0, styled_components_1.default)(style_guide_1.ButtonGroup) `
46
15
  flex: 1;
47
16
  `;
48
- const AuthorActions = ({ author, onSave, onDelete, onCancel, }) => {
49
- const [showDeleteDialog, setShowDeleteDialog] = (0, react_1.useState)(false);
17
+ const StyledIconButton = (0, styled_components_1.default)(style_guide_1.IconButton) `
18
+ color: #0d79d0;
19
+ text-align: center;
20
+ font-size: 14px;
21
+ font-style: normal;
22
+ font-weight: 400;
23
+ line-height: 24px;
24
+ width: auto;
25
+ &:disabled {
26
+ color: #c9c9c9 !important;
27
+ background-color: unset !important;
28
+ border: unset;
29
+ }
30
+ svg {
31
+ margin-right: 4px;
32
+ }
33
+ `;
34
+ const AuthorActions = ({ onSave, onDelete, showDeleteDialog, handleShowDeleteDialog, newAuthor, isDisableSave, }) => {
50
35
  return (react_1.default.createElement(AuthorActionsContainer, { "data-cy": "author-action" },
51
- react_1.default.createElement(DeleteAuthorConfirmationDialog_1.DeleteAuthorConfirmationDialog, { author: author, isOpen: showDeleteDialog, onDelete: () => {
52
- setShowDeleteDialog(true);
36
+ react_1.default.createElement(DeleteAuthorConfirmationDialog_1.DeleteAuthorConfirmationDialog, { isOpen: showDeleteDialog, onDelete: () => {
37
+ handleShowDeleteDialog();
53
38
  onDelete();
54
- }, onCancel: () => setShowDeleteDialog(false) }),
55
- react_1.default.createElement(RemoveButton, { onClick: () => setShowDeleteDialog(true) },
56
- react_1.default.createElement(style_guide_1.DeleteIcon, null)),
39
+ }, onCancel: () => handleShowDeleteDialog() }),
57
40
  react_1.default.createElement(StyledButtonGroup, null,
58
- react_1.default.createElement(style_guide_1.SecondaryButton, { onClick: onCancel }, "Cancel"),
59
- react_1.default.createElement(style_guide_1.PrimaryButton, { onClick: onSave }, "Save"))));
41
+ react_1.default.createElement(StyledIconButton, { onClick: onSave, disabled: isDisableSave, type: "submit" },
42
+ react_1.default.createElement(style_guide_1.PlusIcon, null),
43
+ newAuthor ? 'Save Details' : 'Update Details'))));
60
44
  };
61
45
  exports.AuthorActions = AuthorActions;
@@ -53,6 +53,9 @@ exports.LabelText = styled_components_1.default.div `
53
53
  ${(props) => props.theme.font.family.sans};
54
54
  letter-spacing: -0.2px;
55
55
  color: ${(props) => props.theme.colors.text.primary};
56
+ &::before {
57
+ margin-right: 8px !important;
58
+ }
56
59
  `;
57
60
  exports.Fieldset = styled_components_1.default.fieldset `
58
61
  padding: 0;
@@ -62,7 +65,17 @@ exports.Fieldset = styled_components_1.default.fieldset `
62
65
  const OrcidContainer = styled_components_1.default.div `
63
66
  margin: 16px 0 0;
64
67
  `;
65
- const AuthorDetailsForm = ({ values, onChange, onSave, actionsRef, }) => {
68
+ const TextFieldWithError = (0, styled_components_1.default)(style_guide_1.TextField) `
69
+ &:required::placeholder {
70
+ color: ${(props) => props.theme.colors.text.error};
71
+ }
72
+ `;
73
+ const CheckboxContainer = styled_components_1.default.div `
74
+ display: flex;
75
+ align-items: center;
76
+ gap: 32px;
77
+ `;
78
+ const AuthorDetailsForm = ({ values, onChange, onSave, actionsRef, isEmailRequired, }) => {
66
79
  const formRef = (0, react_1.useRef)(null);
67
80
  if (actionsRef && !actionsRef.current) {
68
81
  actionsRef.current = {
@@ -72,22 +85,28 @@ const AuthorDetailsForm = ({ values, onChange, onSave, actionsRef, }) => {
72
85
  },
73
86
  };
74
87
  }
75
- return (react_1.default.createElement(formik_1.Formik, { initialValues: values, onSubmit: onSave, enableReinitialize: true, validateOnChange: false, innerRef: formRef }, (formik) => {
88
+ return (react_1.default.createElement(formik_1.Formik, { initialValues: values, onSubmit: onSave, enableReinitialize: true, validateOnChange: true, innerRef: formRef }, (formik) => {
76
89
  const isAuthor = formik.values.role === 'author';
77
90
  return (react_1.default.createElement(ChangeHandlingForm_1.ChangeHandlingForm, { onChange: onChange },
78
91
  react_1.default.createElement(exports.Fieldset, null,
79
92
  react_1.default.createElement(style_guide_1.TextFieldGroupContainer, null,
80
93
  react_1.default.createElement(formik_1.Field, { name: 'bibliographicName.given' }, (props) => (react_1.default.createElement(style_guide_1.TextField, Object.assign({ id: 'given-name', placeholder: 'Given name' }, props.field)))),
81
94
  react_1.default.createElement(formik_1.Field, { name: 'bibliographicName.family' }, (props) => (react_1.default.createElement(style_guide_1.TextField, Object.assign({ id: 'family-name', placeholder: 'Family name' }, props.field))))),
82
- react_1.default.createElement(formik_1.Field, { name: 'email', type: 'email' }, (props) => (react_1.default.createElement(style_guide_1.TextField, Object.assign({ id: 'email', placeholder: 'Email address' }, props.field)))),
83
- react_1.default.createElement(style_guide_1.CheckboxLabel, { disabled: !isAuthor },
84
- react_1.default.createElement(formik_1.Field, { name: 'isCorresponding' }, (props) => (react_1.default.createElement(style_guide_1.CheckboxField, Object.assign({ id: 'isCorresponding', checked: props.field.value, disabled: !isAuthor }, props.field)))),
85
- react_1.default.createElement(exports.LabelText, null, "Corresponding Author")),
86
- react_1.default.createElement(style_guide_1.CheckboxLabel, null,
87
- react_1.default.createElement(formik_1.Field, { name: 'role', type: 'checkbox' }, (props) => (react_1.default.createElement(style_guide_1.CheckboxField, { name: 'role', checked: isAuthor, onChange: (e) => {
88
- formik.setFieldValue(props.field.name, e.target.checked ? 'author' : 'other', false);
89
- } }))),
90
- react_1.default.createElement(exports.LabelText, null, "Include in Authors List")),
95
+ react_1.default.createElement(formik_1.Field, { name: 'email', type: 'email' }, (props) => {
96
+ const placeholder = isEmailRequired
97
+ ? '*Email address (required)'
98
+ : 'Email address';
99
+ return (react_1.default.createElement(TextFieldWithError, Object.assign({ required: isEmailRequired, id: 'email', placeholder: placeholder }, props.field)));
100
+ }),
101
+ react_1.default.createElement(CheckboxContainer, null,
102
+ react_1.default.createElement(style_guide_1.CheckboxLabel, { disabled: !isAuthor },
103
+ react_1.default.createElement(formik_1.Field, { name: 'isCorresponding' }, (props) => (react_1.default.createElement(style_guide_1.CheckboxField, Object.assign({ id: 'isCorresponding', checked: props.field.value, disabled: !isAuthor }, props.field)))),
104
+ react_1.default.createElement(exports.LabelText, null, "Corresponding Author")),
105
+ react_1.default.createElement(style_guide_1.CheckboxLabel, null,
106
+ react_1.default.createElement(formik_1.Field, { name: 'role', type: 'checkbox' }, (props) => (react_1.default.createElement(style_guide_1.CheckboxField, { name: 'role', checked: isAuthor, onChange: (e) => {
107
+ formik.setFieldValue(props.field.name, e.target.checked ? 'author' : 'other', false);
108
+ } }))),
109
+ react_1.default.createElement(exports.LabelText, null, "Include in Authors List"))),
91
110
  react_1.default.createElement(OrcidContainer, null,
92
111
  react_1.default.createElement(style_guide_1.TextFieldLabel, null,
93
112
  react_1.default.createElement(exports.LabelText, null, "ORCID"),
@@ -28,10 +28,19 @@ const AuthorListContainer = styled_components_1.default.div `
28
28
  flex: 1;
29
29
  overflow-y: visible;
30
30
  `;
31
- const AuthorList = ({ author, authors, onSelect, moveAuthor, }) => {
31
+ const AuthorListTitle = styled_components_1.default.h2 `
32
+ color: #6e6e6e;
33
+ font-size: 18px;
34
+ font-weight: 400;
35
+ line-height: 24px;
36
+ margin-left: 14px;
37
+ margin-top: 28px;
38
+ `;
39
+ const AuthorList = ({ author, authors, onSelect, onDelete, moveAuthor, lastSavedAuthor, }) => {
32
40
  return (react_1.default.createElement(react_dnd_1.DndProvider, { backend: react_dnd_html5_backend_1.HTML5Backend },
41
+ react_1.default.createElement(AuthorListTitle, null, "Existing Authors"),
33
42
  react_1.default.createElement(AuthorListContainer, { "data-cy": "authors-list" }, authors.map((a) => {
34
- return (react_1.default.createElement(DraggableAuthor_1.DraggableAuthor, { key: a.id, author: a, isSelected: a.id === (author === null || author === void 0 ? void 0 : author.id), onClick: () => onSelect(a), moveAuthor: moveAuthor }));
43
+ return (react_1.default.createElement(DraggableAuthor_1.DraggableAuthor, { key: a.id, author: a, isSelected: a.id === (author === null || author === void 0 ? void 0 : author.id), onClick: () => onSelect(a), onDelete: () => onDelete(), moveAuthor: moveAuthor, showSuccessIcon: lastSavedAuthor === a.id }));
35
44
  }))));
36
45
  };
37
46
  exports.AuthorList = AuthorList;
@@ -54,12 +54,13 @@ const AuthorAffiliations_1 = require("./AuthorAffiliations");
54
54
  const AuthorDetailsForm_1 = require("./AuthorDetailsForm");
55
55
  const AuthorFormPlaceholder_1 = require("./AuthorFormPlaceholder");
56
56
  const AuthorList_1 = require("./AuthorList");
57
+ const RequiredFieldConfirmationDialog_1 = require("./RequiredFieldConfirmationDialog");
57
58
  const SaveAuthorConfirmationDialog_1 = require("./SaveAuthorConfirmationDialog");
58
59
  const AddAuthorButton = styled_components_1.default.div `
59
60
  display: flex;
60
61
  align-items: center;
61
62
  margin-bottom: ${(props) => props.theme.grid.unit * 4}px;
62
- margin-left: ${(props) => props.theme.grid.unit * 2}px;
63
+ margin-left: ${(props) => props.theme.grid.unit * 4}px;
63
64
  cursor: pointer;
64
65
  `;
65
66
  const ActionTitle = styled_components_1.default.div `
@@ -79,6 +80,9 @@ const AuthorForms = styled_components_1.default.div `
79
80
  padding-left: ${(props) => props.theme.grid.unit * 3}px;
80
81
  padding-right: ${(props) => props.theme.grid.unit * 3}px;
81
82
  `;
83
+ const StyledSidebarContent = (0, styled_components_1.default)(style_guide_1.SidebarContent) `
84
+ padding: 0;
85
+ `;
82
86
  const authorsReducer = (0, array_reducer_1.arrayReducer)((a, b) => a.id === b.id);
83
87
  const affiliationsReducer = (0, array_reducer_1.arrayReducer)((a, b) => a.id === b.id);
84
88
  const normalize = (author) => ({
@@ -96,38 +100,97 @@ const normalize = (author) => ({
96
100
  footnote: author.footnote || [],
97
101
  corresp: author.corresp || [],
98
102
  });
99
- const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, author, onSaveAuthor, onDeleteAuthor, onSaveAffiliation, }) => {
103
+ const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, author, onSaveAuthor, onDeleteAuthor, onSaveAffiliation, addNewAuthor = false, }) => {
100
104
  const [isOpen, setOpen] = (0, react_1.useState)(true);
105
+ const [isDisableSave, setDisableSave] = (0, react_1.useState)(false);
106
+ const [isEmailRequired, setEmailRequired] = (0, react_1.useState)(false);
101
107
  const [showConfirmationDialog, setShowConfirmationDialog] = (0, react_1.useState)(false);
108
+ const [showRequiredFieldConfirmationDialog, setShowRequiredFieldConfirmationDialog,] = (0, react_1.useState)(false);
109
+ const [lastSavedAuthor, setLastSavedAuthor] = (0, react_1.useState)(null);
110
+ const [showDeleteDialog, setShowDeleteDialog] = (0, react_1.useState)(false);
111
+ const [newAuthor, setNewAuthor] = (0, react_1.useState)(false);
112
+ const [unSavedChanges, setUnSavedChanges] = (0, react_1.useState)(false);
113
+ const [nextAuthor, setNextAuthor] = (0, react_1.useState)(null);
102
114
  const valuesRef = (0, react_1.useRef)();
103
115
  const actionsRef = (0, react_1.useRef)();
104
116
  const [authors, dispatchAuthors] = (0, react_1.useReducer)(authorsReducer, $authors.sort(authors_1.authorComparator));
105
117
  const [affiliations, dispatchAffiliations] = (0, react_1.useReducer)(affiliationsReducer, $affiliations);
118
+ (0, react_1.useEffect)(() => {
119
+ if (addNewAuthor) {
120
+ handleAddAuthor();
121
+ }
122
+ }, [addNewAuthor]);
106
123
  const [selection, setSelection] = (0, react_1.useState)(author);
107
124
  const handleSelect = (author) => {
108
125
  const values = valuesRef.current;
109
- if (values && selection && !(0, lodash_1.isEqual)(values, normalize(selection))) {
126
+ if (values &&
127
+ selection &&
128
+ !(0, lodash_1.isEqual)(values, normalize(selection)) &&
129
+ !isDisableSave) {
110
130
  setShowConfirmationDialog(true);
131
+ setNextAuthor(author);
132
+ }
133
+ else if (isDisableSave && unSavedChanges) {
134
+ setShowRequiredFieldConfirmationDialog(true);
135
+ setNextAuthor(author);
111
136
  }
112
137
  else {
113
138
  setSelection(author);
114
139
  }
115
140
  };
116
- const handleClose = () => {
141
+ (0, react_1.useEffect)(() => {
117
142
  const values = valuesRef.current;
118
143
  if (values && selection && !(0, lodash_1.isEqual)(values, normalize(selection))) {
144
+ setUnSavedChanges(true);
145
+ }
146
+ }, [valuesRef.current]);
147
+ const handleClose = () => {
148
+ if (isDisableSave && unSavedChanges) {
149
+ setShowRequiredFieldConfirmationDialog(true);
150
+ }
151
+ else if (unSavedChanges) {
119
152
  setShowConfirmationDialog(true);
120
153
  }
121
154
  else {
155
+ setShowRequiredFieldConfirmationDialog(false);
156
+ setLastSavedAuthor(null);
122
157
  setOpen(false);
123
158
  }
124
159
  };
160
+ const handleSave = () => {
161
+ if (valuesRef.current && selection) {
162
+ handleSaveAuthor(valuesRef.current);
163
+ }
164
+ if (nextAuthor) {
165
+ setSelection(nextAuthor);
166
+ setNextAuthor(null);
167
+ }
168
+ else if (newAuthor) {
169
+ createNewAuthor();
170
+ setNewAuthor(false);
171
+ }
172
+ setShowConfirmationDialog(false);
173
+ };
174
+ const handleCancel = () => {
175
+ handleResetAuthor();
176
+ if (nextAuthor) {
177
+ setSelection(nextAuthor);
178
+ setNextAuthor(null);
179
+ }
180
+ else if (newAuthor) {
181
+ createNewAuthor();
182
+ setNewAuthor(false);
183
+ }
184
+ setShowConfirmationDialog(false);
185
+ };
125
186
  const handleSaveAuthor = (values) => {
126
187
  if (!values || !selection) {
127
188
  return;
128
189
  }
129
190
  const author = Object.assign(Object.assign({}, selection), values);
130
191
  onSaveAuthor(author);
192
+ setLastSavedAuthor(author.id);
193
+ setUnSavedChanges(false);
131
194
  setSelection(author);
132
195
  setShowConfirmationDialog(false);
133
196
  dispatchAuthors({
@@ -150,7 +213,7 @@ const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, author,
150
213
  state: copy,
151
214
  });
152
215
  };
153
- const handleAddAuthor = () => {
216
+ const createNewAuthor = () => {
154
217
  const name = (0, json_schema_1.buildBibliographicName)({ given: '', family: '' });
155
218
  const author = {
156
219
  id: (0, json_schema_1.generateID)(json_schema_1.ObjectTypes.Contributor),
@@ -167,12 +230,18 @@ const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, author,
167
230
  corresp: [],
168
231
  footnote: [],
169
232
  };
170
- onSaveAuthor(author);
171
233
  setSelection(author);
172
- dispatchAuthors({
173
- type: 'update',
174
- items: [author],
175
- });
234
+ };
235
+ const handleAddAuthor = () => {
236
+ const values = valuesRef.current;
237
+ if (values && selection && !(0, lodash_1.isEqual)(values, normalize(selection))) {
238
+ setShowConfirmationDialog(true);
239
+ setNextAuthor(null);
240
+ setNewAuthor(true);
241
+ }
242
+ else {
243
+ createNewAuthor();
244
+ }
176
245
  };
177
246
  const handleDeleteAuthor = () => {
178
247
  if (!selection) {
@@ -212,9 +281,44 @@ const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, author,
212
281
  var _a;
213
282
  (_a = actionsRef.current) === null || _a === void 0 ? void 0 : _a.reset();
214
283
  setShowConfirmationDialog(false);
284
+ setShowRequiredFieldConfirmationDialog(false);
285
+ setUnSavedChanges(false);
286
+ if (!newAuthor && !nextAuthor) {
287
+ setLastSavedAuthor(null);
288
+ setOpen(false);
289
+ }
290
+ else {
291
+ setNewAuthor(false);
292
+ createNewAuthor();
293
+ }
215
294
  };
216
295
  const handleChangeAuthor = (values) => {
296
+ const isUnchanged = (0, lodash_1.isEqual)(normalize(selection), values);
217
297
  valuesRef.current = values;
298
+ const { given, family } = values.bibliographicName;
299
+ const { email, isCorresponding } = values;
300
+ const isNameFilled = (given === null || given === void 0 ? void 0 : given.length) && (family === null || family === void 0 ? void 0 : family.length);
301
+ if (isNameFilled && !isCorresponding && !isUnchanged) {
302
+ setDisableSave(false);
303
+ }
304
+ else if (isCorresponding &&
305
+ (email === null || email === void 0 ? void 0 : email.length) &&
306
+ isNameFilled &&
307
+ !isUnchanged) {
308
+ setDisableSave(false);
309
+ }
310
+ else {
311
+ setDisableSave(true);
312
+ }
313
+ if (isCorresponding && !(email === null || email === void 0 ? void 0 : email.length)) {
314
+ setEmailRequired(true);
315
+ }
316
+ else {
317
+ setEmailRequired(false);
318
+ }
319
+ };
320
+ const handleShowDeleteDialog = () => {
321
+ setShowDeleteDialog((prev) => !prev);
218
322
  };
219
323
  return (react_1.default.createElement(style_guide_1.StyledModal, { isOpen: isOpen, onRequestClose: () => handleClose(), shouldCloseOnOverlayClick: true },
220
324
  react_1.default.createElement(style_guide_1.ModalContainer, null,
@@ -224,16 +328,17 @@ const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, author,
224
328
  react_1.default.createElement(style_guide_1.ModalSidebar, { "data-cy": "authors-sidebar" },
225
329
  react_1.default.createElement(style_guide_1.ModalSidebarHeader, null,
226
330
  react_1.default.createElement(style_guide_1.ModalSidebarTitle, null, "Authors")),
227
- react_1.default.createElement(style_guide_1.SidebarContent, null,
331
+ react_1.default.createElement(StyledSidebarContent, null,
228
332
  react_1.default.createElement(AddAuthorButton, { "data-cy": "add-author-button", onClick: handleAddAuthor },
229
- react_1.default.createElement(style_guide_1.AddIcon, { width: 40, height: 40 }),
230
- react_1.default.createElement(ActionTitle, null, "Add Author")),
231
- react_1.default.createElement(AuthorList_1.AuthorList, { author: selection, authors: authors, onSelect: handleSelect, moveAuthor: handleMoveAuthor }))),
333
+ react_1.default.createElement(style_guide_1.AddIcon, { width: 18, height: 18 }),
334
+ react_1.default.createElement(ActionTitle, null, "New Author")),
335
+ react_1.default.createElement(AuthorList_1.AuthorList, { author: selection, authors: authors, onSelect: handleSelect, onDelete: handleShowDeleteDialog, moveAuthor: handleMoveAuthor, lastSavedAuthor: lastSavedAuthor }))),
232
336
  react_1.default.createElement(style_guide_1.ScrollableModalContent, { "data-cy": "author-modal-content" }, selection ? (react_1.default.createElement(AuthorForms, null,
233
- react_1.default.createElement(SaveAuthorConfirmationDialog_1.SaveAuthorConfirmationDialog, { isOpen: showConfirmationDialog, onSave: () => handleSaveAuthor(valuesRef.current), onCancel: handleResetAuthor }),
234
- react_1.default.createElement(AuthorActions_1.AuthorActions, { author: selection, onSave: () => handleSaveAuthor(valuesRef.current), onDelete: handleDeleteAuthor, onCancel: () => setOpen(false) }),
337
+ react_1.default.createElement(RequiredFieldConfirmationDialog_1.RequiredFieldConfirmationDialog, { isOpen: showRequiredFieldConfirmationDialog, onSave: () => setShowRequiredFieldConfirmationDialog(false), onCancel: handleCancel }),
338
+ react_1.default.createElement(SaveAuthorConfirmationDialog_1.SaveAuthorConfirmationDialog, { isOpen: showConfirmationDialog, onSave: handleSave, onCancel: handleCancel }),
339
+ react_1.default.createElement(AuthorActions_1.AuthorActions, { onSave: () => handleSaveAuthor(valuesRef.current), onDelete: handleDeleteAuthor, showDeleteDialog: showDeleteDialog, handleShowDeleteDialog: handleShowDeleteDialog, newAuthor: newAuthor, isDisableSave: isDisableSave }),
235
340
  react_1.default.createElement(FormLabel, null, "Details"),
236
- react_1.default.createElement(AuthorDetailsForm_1.AuthorDetailsForm, { values: normalize(selection), onChange: handleChangeAuthor, onSave: handleSaveAuthor, actionsRef: actionsRef }),
341
+ react_1.default.createElement(AuthorDetailsForm_1.AuthorDetailsForm, { values: normalize(selection), onChange: handleChangeAuthor, onSave: handleSaveAuthor, actionsRef: actionsRef, isEmailRequired: isEmailRequired }),
237
342
  react_1.default.createElement(FormLabel, null, "Affiliations"),
238
343
  react_1.default.createElement(AuthorAffiliations_1.AuthorAffiliations, { author: selection, affiliations: affiliations, onSave: handleSaveAffiliation, onAdd: handleAddAffiliation, onRemove: handleRemoveAffiliation }))) : (react_1.default.createElement(AuthorFormPlaceholder_1.AuthorFormPlaceholder, null)))))));
239
344
  };
@@ -6,10 +6,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DeleteAuthorConfirmationDialog = void 0;
7
7
  const style_guide_1 = require("@manuscripts/style-guide");
8
8
  const react_1 = __importDefault(require("react"));
9
- const authors_1 = require("../../lib/authors");
10
- const DeleteAuthorConfirmationDialog = ({ isOpen, author, onDelete, onCancel }) => {
11
- const label = (0, authors_1.authorLabel)(author);
12
- return (react_1.default.createElement(style_guide_1.Dialog, { isOpen: isOpen, category: style_guide_1.Category.confirmation, header: 'Remove author', message: `Are you sure you want to remove ${label} from the authors list?`, actions: {
9
+ const styled_components_1 = __importDefault(require("styled-components"));
10
+ const StyledIcon = (0, styled_components_1.default)(style_guide_1.AttentionOrangeIcon) `
11
+ margin-right: 8px;
12
+ `;
13
+ const P = styled_components_1.default.p `
14
+ margin-bottom: 4px;
15
+ `;
16
+ const Header = () => (react_1.default.createElement(react_1.default.Fragment, null,
17
+ react_1.default.createElement(StyledIcon, null),
18
+ " Delete author"));
19
+ const Message = () => {
20
+ return (react_1.default.createElement(react_1.default.Fragment, null,
21
+ react_1.default.createElement("p", null, "Are you sure you want to remove this author from the authors list?"),
22
+ react_1.default.createElement(P, null, "Do you want to continue?")));
23
+ };
24
+ const DeleteAuthorConfirmationDialog = ({ isOpen, onDelete, onCancel }) => {
25
+ return (react_1.default.createElement(style_guide_1.Dialog, { isOpen: isOpen, category: style_guide_1.Category.confirmation, header: react_1.default.createElement(Header, null), message: react_1.default.createElement(Message, null), actions: {
13
26
  secondary: {
14
27
  action: onDelete,
15
28
  title: 'Delete',
@@ -49,16 +49,15 @@ const styled_components_1 = __importDefault(require("styled-components"));
49
49
  const authors_1 = require("../../lib/authors");
50
50
  const dnd_1 = require("../../lib/dnd");
51
51
  const AuthorContainer = styled_components_1.default.div `
52
- padding: ${(props) => props.theme.grid.unit * 2}px;
52
+ padding: ${(props) => props.theme.grid.unit * 2}px 0
53
+ ${(props) => props.theme.grid.unit * 2}px;
53
54
  display: flex;
54
55
  justify-content: space-between;
55
56
  align-items: center;
56
- cursor: pointer;
57
57
  transition: background-color 0.25s;
58
58
  border: 1px solid transparent;
59
59
  border-left: 0;
60
60
  border-right: 0;
61
-
62
61
  &:hover,
63
62
  &.active {
64
63
  background: ${(props) => props.theme.colors.background.fifth};
@@ -69,20 +68,31 @@ const AuthorContainer = styled_components_1.default.div `
69
68
  }
70
69
 
71
70
  &.dragging {
72
- visibility: hidden;
71
+ opacity: 1;
72
+ cursor: grabbing;
73
+ }
74
+
75
+ &.dragging * {
76
+ opacity: 0;
73
77
  }
74
78
 
75
79
  &.drop-before {
80
+ cursor: grabbing;
76
81
  border-top-color: ${(props) => props.theme.colors.brand.dark};
77
82
  }
78
83
 
79
84
  &.drop-after {
85
+ cursor: grabbing;
80
86
  border-bottom-color: ${(props) => props.theme.colors.brand.dark};
81
87
  }
82
88
  `;
83
89
  const AvatarContainer = styled_components_1.default.div `
84
90
  display: inline-flex;
85
91
  position: relative;
92
+ align-items: center;
93
+ `;
94
+ const Box = styled_components_1.default.div `
95
+ margin-left: 14px;
86
96
  `;
87
97
  const AuthorBadge = styled_components_1.default.div `
88
98
  display: inline-flex;
@@ -103,21 +113,40 @@ const AuthorName = styled_components_1.default.div `
103
113
  margin-left: 12px;
104
114
  flex: 1;
105
115
  `;
106
- const DragHandle = (0, styled_components_1.default)(style_guide_1.VerticalEllipsisIcon) `
116
+ const DragHandle = (0, styled_components_1.default)(style_guide_1.DraggableIcon) `
107
117
  cursor: move;
118
+ margin-left: -4px;
119
+ margin-right: -12px;
108
120
  `;
109
- const DraggableAuthor = ({ author, isSelected, onClick, moveAuthor, }) => {
121
+ const RemoveButton = styled_components_1.default.div `
122
+ display: flex;
123
+ align-items: center;
124
+ margin-right: 8px;
125
+ svg {
126
+ cursor: pointer;
127
+ }
128
+ .icon_element {
129
+ fill: #6e6e6e;
130
+ }
131
+ `;
132
+ const StyledCrclTickAnimation = (0, styled_components_1.default)(style_guide_1.CrclTickAnimation) `
133
+ margin-left: 12px;
134
+ `;
135
+ exports.DraggableAuthor = react_1.default.memo(({ author, isSelected, onClick, onDelete, moveAuthor, showSuccessIcon }) => {
110
136
  const [dropSide, setDropSide] = (0, react_1.useState)();
111
137
  const ref = (0, react_1.useRef)(null);
112
- const [{ isDragging }, dragRef] = (0, react_dnd_1.useDrag)({
138
+ const [{ isDragging }, dragRef, preview] = (0, react_dnd_1.useDrag)({
113
139
  type: 'author',
114
140
  item: {
115
141
  author,
116
142
  },
117
- collect: (monitor) => ({
118
- isDragging: monitor.isDragging(),
119
- }),
143
+ collect: (monitor) => {
144
+ return {
145
+ isDragging: monitor.isDragging(),
146
+ };
147
+ },
120
148
  });
149
+ preview(ref);
121
150
  const [{ isOver }, dropRef] = (0, react_dnd_1.useDrop)({
122
151
  accept: 'author',
123
152
  hover: (item, monitor) => {
@@ -151,13 +180,31 @@ const DraggableAuthor = ({ author, isSelected, onClick, moveAuthor, }) => {
151
180
  }
152
181
  if (isSelected) {
153
182
  classes.push('active');
183
+ if (ref.current) {
184
+ ref.current.scrollIntoView({
185
+ behavior: 'smooth',
186
+ block: 'center',
187
+ });
188
+ }
154
189
  }
155
- return (react_1.default.createElement(AuthorContainer, { ref: ref, className: classes.join(' '), onClick: onClick, "data-cy": "author-item" },
190
+ (0, react_1.useEffect)(() => {
191
+ if (isDragging || isOver) {
192
+ document.body.style.cursor = 'grabbing';
193
+ }
194
+ return () => {
195
+ document.body.style.cursor = 'default';
196
+ };
197
+ }, [isDragging, isOver]);
198
+ return (react_1.default.createElement(AuthorContainer, { ref: ref, className: classes.join(' '), onClick: onClick, "data-cy": "author-item", style: { cursor: isDragging ? 'grabbing' : 'default' } },
156
199
  react_1.default.createElement(AvatarContainer, { "data-cy": "author-avatar" },
157
- react_1.default.createElement(style_guide_1.Avatar, { size: 36, color: '#6e6e6e' }),
200
+ isSelected && react_1.default.createElement(DragHandle, null),
201
+ react_1.default.createElement(Box, null,
202
+ showSuccessIcon && react_1.default.createElement(StyledCrclTickAnimation, { size: 36 }),
203
+ react_1.default.createElement(style_guide_1.Avatar, { size: 36, color: '#6e6e6e', opacity: showSuccessIcon ? 0.05 : 1 })),
158
204
  react_1.default.createElement(AuthorNotes, { "data-cy": "author-notes" }, author.isCorresponding && (react_1.default.createElement(AuthorBadge, null,
159
205
  react_1.default.createElement(style_guide_1.CorrespondingAuthorIcon, null))))),
160
206
  react_1.default.createElement(AuthorName, { "data-cy": "author-name" }, (0, authors_1.authorLabel)(author)),
161
- isSelected && react_1.default.createElement(DragHandle, null)));
162
- };
163
- exports.DraggableAuthor = DraggableAuthor;
207
+ isSelected && (react_1.default.createElement(RemoveButton, { onClick: () => onDelete(), "data-tooltip-id": 'delete-button-tooltip' },
208
+ react_1.default.createElement(style_guide_1.DeleteIcon, { fill: '#6E6E6E' }),
209
+ react_1.default.createElement(style_guide_1.Tooltip, { id: 'delete-button-tooltip', place: "bottom" }, 'Delete')))));
210
+ });