@manuscripts/body-editor 3.12.16 → 3.12.19

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 (45) hide show
  1. package/dist/cjs/commands.js +3 -4
  2. package/dist/cjs/components/affiliations/AffiliationForm.js +5 -6
  3. package/dist/cjs/components/affiliations/AffiliationsModal.js +21 -57
  4. package/dist/cjs/components/affiliations/AffiliationsPanel.js +22 -0
  5. package/dist/cjs/components/authors/AuthorDetailsForm.js +3 -4
  6. package/dist/cjs/components/authors/AuthorsModal.js +15 -48
  7. package/dist/cjs/components/authors/AuthorsPanel.js +18 -0
  8. package/dist/cjs/components/authors/useManageAffiliations.js +6 -4
  9. package/dist/cjs/components/authors-affiliations/AuthorsAndAffiliationsModals.js +134 -0
  10. package/dist/cjs/components/authors-affiliations/GenericPanel.js +136 -0
  11. package/dist/cjs/components/form/ModalFormActions.js +11 -5
  12. package/dist/cjs/components/references/ReferenceForm/ReferenceForm.js +2 -2
  13. package/dist/cjs/versions.js +1 -1
  14. package/dist/cjs/views/affiliations.js +5 -11
  15. package/dist/cjs/views/contributors.js +4 -7
  16. package/dist/es/commands.js +3 -4
  17. package/dist/es/components/affiliations/AffiliationForm.js +5 -6
  18. package/dist/es/components/affiliations/AffiliationsModal.js +21 -53
  19. package/dist/es/components/affiliations/AffiliationsPanel.js +15 -0
  20. package/dist/es/components/authors/AuthorDetailsForm.js +3 -4
  21. package/dist/es/components/authors/AuthorsModal.js +15 -45
  22. package/dist/es/components/authors/AuthorsPanel.js +11 -0
  23. package/dist/es/components/authors/useManageAffiliations.js +7 -5
  24. package/dist/es/components/authors-affiliations/AuthorsAndAffiliationsModals.js +93 -0
  25. package/dist/es/components/authors-affiliations/GenericPanel.js +94 -0
  26. package/dist/es/components/form/ModalFormActions.js +11 -5
  27. package/dist/es/components/references/ReferenceForm/ReferenceForm.js +2 -2
  28. package/dist/es/versions.js +1 -1
  29. package/dist/es/views/affiliations.js +5 -11
  30. package/dist/es/views/contributors.js +5 -8
  31. package/dist/types/components/affiliations/AffiliationForm.d.ts +1 -0
  32. package/dist/types/components/affiliations/AffiliationsModal.d.ts +2 -5
  33. package/dist/types/components/affiliations/AffiliationsPanel.d.ts +11 -0
  34. package/dist/types/components/authors/AuthorDetailsForm.d.ts +1 -0
  35. package/dist/types/components/authors/AuthorsModal.d.ts +2 -4
  36. package/dist/types/components/authors/AuthorsPanel.d.ts +13 -0
  37. package/dist/types/components/authors/useManageAffiliations.d.ts +0 -2
  38. package/dist/types/components/authors-affiliations/AuthorsAndAffiliationsModals.d.ts +29 -0
  39. package/dist/types/components/authors-affiliations/GenericPanel.d.ts +48 -0
  40. package/dist/types/components/form/ModalFormActions.d.ts +1 -0
  41. package/dist/types/versions.d.ts +1 -1
  42. package/package.json +1 -1
  43. package/dist/cjs/components/authors/AffiliationDrawer.js +0 -24
  44. package/dist/es/components/authors/AffiliationDrawer.js +0 -17
  45. package/dist/types/components/authors/AffiliationDrawer.d.ts +0 -12
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ /*!
3
+ * © 2026 Atypon Systems LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ var __importDefault = (this && this.__importDefault) || function (mod) {
51
+ return (mod && mod.__esModule) ? mod : { "default": mod };
52
+ };
53
+ Object.defineProperty(exports, "__esModule", { value: true });
54
+ exports.ListItem = exports.GenericPanel = exports.ListItems = void 0;
55
+ exports.useListSelectedIds = useListSelectedIds;
56
+ const style_guide_1 = require("@manuscripts/style-guide");
57
+ const react_1 = __importStar(require("react"));
58
+ const styled_components_1 = __importDefault(require("styled-components"));
59
+ exports.ListItems = style_guide_1.DrawerItemsList;
60
+ const Panel = styled_components_1.default.div `
61
+ display: flex;
62
+ flex-direction: column;
63
+ `;
64
+ const PanelHeader = styled_components_1.default.div `
65
+ display: flex;
66
+ align-items: flex-start;
67
+ padding: ${(props) => props.theme.grid.unit * 2}px
68
+ ${(props) => props.theme.grid.unit * 4}px;
69
+ `;
70
+ const PanelTitle = styled_components_1.default.span `
71
+ font-family: ${(props) => props.theme.font.family.sans};
72
+ font-size: 18px;
73
+ line-height: 24px;
74
+ letter-spacing: -0.37px;
75
+ color: ${(props) => props.theme.colors.text.secondary};
76
+ `;
77
+ const PanelCreateButton = (0, styled_components_1.default)(style_guide_1.IconButton) `
78
+ color: #0d79d0;
79
+ font-size: 14px;
80
+ font-weight: 400;
81
+ font-style: normal;
82
+ line-height: 1;
83
+ width: auto;
84
+ height: 24px;
85
+ margin-left: auto;
86
+ svg {
87
+ margin-right: 4px;
88
+ }
89
+ `;
90
+ const PanelEmpty = styled_components_1.default.div `
91
+ display: flex;
92
+ flex-direction: column;
93
+ align-items: center;
94
+ justify-content: center;
95
+ gap: 16px;
96
+ padding: ${(props) => props.theme.grid.unit * 20}px
97
+ ${(props) => props.theme.grid.unit * 4}px;
98
+ `;
99
+ const PanelEmptyIcon = styled_components_1.default.div `
100
+ width: 120px;
101
+ height: 120px;
102
+ flex-shrink: 0;
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: center;
106
+ svg {
107
+ width: 100%;
108
+ height: 100%;
109
+ }
110
+ `;
111
+ const PanelEmptyText = styled_components_1.default.p `
112
+ margin: 0;
113
+ font-size: 18px;
114
+ line-height: 24px;
115
+ text-align: center;
116
+ letter-spacing: -0.369px;
117
+ `;
118
+ const GenericPanel = ({ title, createLabel, onCreate, createDataCy, emptyDataCy, isEmpty, emptyIcon, emptyMessage, children, }) => (react_1.default.createElement(Panel, null,
119
+ react_1.default.createElement(PanelHeader, null,
120
+ react_1.default.createElement(PanelTitle, null, title),
121
+ react_1.default.createElement(PanelCreateButton, { onClick: onCreate, "data-cy": createDataCy },
122
+ react_1.default.createElement(style_guide_1.PlusIcon, null),
123
+ createLabel)),
124
+ isEmpty ? (react_1.default.createElement(PanelEmpty, { "data-cy": emptyDataCy },
125
+ react_1.default.createElement(PanelEmptyIcon, { "aria-hidden": true }, emptyIcon),
126
+ react_1.default.createElement(PanelEmptyText, null, emptyMessage))) : (children)));
127
+ exports.GenericPanel = GenericPanel;
128
+ const ListItem = ({ selected, onClick, primary, secondary, }) => (react_1.default.createElement(style_guide_1.DrawerListItem, { "data-cy": "item", selected: selected, onClick: onClick },
129
+ react_1.default.createElement(style_guide_1.DrawerIcon, null, selected ? (react_1.default.createElement(style_guide_1.AddedIcon, { width: 22, height: 22 })) : (react_1.default.createElement(style_guide_1.AddIcon, { width: 22, height: 22 }))),
130
+ react_1.default.createElement(style_guide_1.DrawerLabelContainer, null,
131
+ react_1.default.createElement(style_guide_1.DrawerItemLabel, null, primary),
132
+ secondary != null && secondary !== '' ? (react_1.default.createElement(style_guide_1.DrawerItemMeta, null, secondary)) : null)));
133
+ exports.ListItem = ListItem;
134
+ function useListSelectedIds(selectedItems) {
135
+ return (0, react_1.useMemo)(() => new Set(selectedItems.map((x) => x.id)), [selectedItems]);
136
+ }
@@ -35,15 +35,21 @@ const StyledIconButton = (0, styled_components_1.default)(style_guide_1.IconButt
35
35
  margin-right: 4px;
36
36
  }
37
37
  `;
38
- const ModalFormActions = ({ type, form, onDelete, showingDeleteDialog, showDeleteDialog, newEntity, isDisableSave, }) => {
38
+ const ModalFormActions = ({ type, form, onDelete, showingDeleteDialog, showDeleteDialog, newEntity, isDisableSave, onSubmitForm, }) => {
39
+ const saveButton = onSubmitForm ? (react_1.default.createElement(StyledIconButton, { disabled: isDisableSave, type: "button", onClick: () => {
40
+ if (!isDisableSave) {
41
+ void onSubmitForm();
42
+ }
43
+ } },
44
+ react_1.default.createElement(style_guide_1.PlusIcon, null),
45
+ newEntity ? 'Save Details' : 'Update Details')) : (react_1.default.createElement(StyledIconButton, { disabled: isDisableSave, type: "submit", form: form },
46
+ react_1.default.createElement(style_guide_1.PlusIcon, null),
47
+ newEntity ? 'Save Details' : 'Update Details'));
39
48
  return (react_1.default.createElement(ActionsContainer, { "data-cy": `${type}-action` },
40
49
  react_1.default.createElement(ConfirmationDialog_1.ConfirmationDialog, { isOpen: showingDeleteDialog, onPrimary: () => {
41
50
  onDelete();
42
51
  showDeleteDialog();
43
52
  }, onSecondary: showDeleteDialog, type: ConfirmationDialog_1.DialogType.DELETE, entityType: type }),
44
- react_1.default.createElement(StyledButtonGroup, null,
45
- react_1.default.createElement(StyledIconButton, { disabled: isDisableSave, type: "submit", form: form },
46
- react_1.default.createElement(style_guide_1.PlusIcon, null),
47
- newEntity ? 'Save Details' : 'Update Details'))));
53
+ react_1.default.createElement(StyledButtonGroup, null, saveButton)));
48
54
  };
49
55
  exports.ModalFormActions = ModalFormActions;
@@ -153,8 +153,8 @@ const ReferenceForm = ({ values, showDelete, onChange, onDelete, onCancel, onSav
153
153
  react_1.default.createElement(style_guide_1.AddAuthorIcon, { height: 17, width: 17 })),
154
154
  react_1.default.createElement("div", null, formik.values.editor?.map((editor, index) => (react_1.default.createElement(PersonDropDown_1.PersonDropDown, { key: index, index: index, person: editor, isNew: newEditorIndex === index, remove: remove, onChange: formik.handleChange, type: "editor" })))))) })),
155
155
  (0, utils_1.shouldRenderField)('issued', formik.values.type) && (react_1.default.createElement(style_guide_1.FormRow, null,
156
- react_1.default.createElement(style_guide_1.Label, { htmlFor: "issued['date-parts'][0][0]" }, "Issued (Year)"),
157
- react_1.default.createElement(style_guide_1.YearField, { name: "issued['date-parts'][0][0]", type: 'number', step: 1, onChange: (event) => {
156
+ react_1.default.createElement(style_guide_1.Label, { htmlFor: "issued-year-field" }, "Issued (Year)"),
157
+ react_1.default.createElement(style_guide_1.YearField, { name: "issued['date-parts'][0][0]", id: "issued-year-field", type: 'number', step: 1, onChange: (event) => {
158
158
  const { value } = event.target;
159
159
  if (value) {
160
160
  if (formik.values.issued) {
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MATHJAX_VERSION = exports.VERSION = void 0;
4
- exports.VERSION = '3.12.16';
4
+ exports.VERSION = '3.12.19';
5
5
  exports.MATHJAX_VERSION = '3.2.2';
@@ -23,7 +23,7 @@ const style_guide_1 = require("@manuscripts/style-guide");
23
23
  const track_changes_plugin_1 = require("@manuscripts/track-changes-plugin");
24
24
  const transform_1 = require("@manuscripts/transform");
25
25
  const prosemirror_state_1 = require("prosemirror-state");
26
- const AffiliationsModal_1 = require("../components/affiliations/AffiliationsModal");
26
+ const AuthorsAndAffiliationsModals_1 = require("../components/authors-affiliations/AuthorsAndAffiliationsModals");
27
27
  const icons_1 = require("../icons");
28
28
  const authors_1 = require("../lib/authors");
29
29
  const comments_1 = require("../lib/comments");
@@ -58,22 +58,16 @@ class AffiliationsView extends block_view_1.default {
58
58
  };
59
59
  this.handleEdit = (id, addNew) => {
60
60
  this.props.popper.destroy();
61
- const contributors = (0, view_1.findChildrenAttrsByType)(this.view, transform_1.schema.nodes.contributor);
62
61
  const affiliations = (0, view_1.findChildrenAttrsByType)(this.view, transform_1.schema.nodes.affiliation);
63
- const affiliation = id
64
- ? affiliations.filter((a) => a.id === id)[0]
65
- : undefined;
62
+ const affiliation = id ? affiliations.find((a) => a.id === id) : undefined;
66
63
  const componentProps = {
64
+ initialModal: 'affiliations',
65
+ view: this.view,
67
66
  affiliation,
68
- authors: contributors,
69
- affiliations,
70
- onSaveAffiliation: (affiliation) => (0, AffiliationsModal_1.handleSaveAffiliation)(this.view, affiliation, this.getPos()),
71
- onDeleteAffiliation: (affiliation) => (0, AffiliationsModal_1.handleDeleteAffiliation)(this.view, affiliation),
72
- onUpdateAuthors: (authors) => (0, AffiliationsModal_1.handleUpdateAuthors)(this.view, authors),
73
67
  addNewAffiliation: addNew,
74
68
  };
75
69
  this.popper?.remove();
76
- this.popper = (0, ReactSubView_1.default)(this.props, AffiliationsModal_1.AffiliationsModal, componentProps, this.node, this.getPos, this.view);
70
+ this.popper = (0, ReactSubView_1.default)(this.props, AuthorsAndAffiliationsModals_1.AuthorsAndAffiliationsModals, componentProps, this.node, this.getPos, this.view);
77
71
  this.container.appendChild(this.popper);
78
72
  };
79
73
  this.showGroupContextMenu = () => {
@@ -23,7 +23,7 @@ const style_guide_1 = require("@manuscripts/style-guide");
23
23
  const track_changes_plugin_1 = require("@manuscripts/track-changes-plugin");
24
24
  const transform_1 = require("@manuscripts/transform");
25
25
  const prosemirror_state_1 = require("prosemirror-state");
26
- const AuthorsModal_1 = require("../components/authors/AuthorsModal");
26
+ const AuthorsAndAffiliationsModals_1 = require("../components/authors-affiliations/AuthorsAndAffiliationsModals");
27
27
  const authors_1 = require("../lib/authors");
28
28
  const comments_1 = require("../lib/comments");
29
29
  const navigation_utils_1 = require("../lib/navigation-utils");
@@ -216,18 +216,15 @@ class ContributorsView extends block_view_1.default {
216
216
  this.handleEdit = (id, addNew) => {
217
217
  this.props.popper.destroy();
218
218
  const contributors = (0, view_1.findChildrenAttrsByType)(this.view, transform_1.schema.nodes.contributor);
219
- const affiliations = (0, view_1.findChildrenAttrsByType)(this.view, transform_1.schema.nodes.affiliation);
220
219
  const author = id ? contributors.filter((a) => a.id === id)[0] : undefined;
221
220
  const componentProps = {
221
+ initialModal: 'authors',
222
+ view: this.view,
222
223
  author,
223
- authors: contributors,
224
- affiliations,
225
- onSaveAuthor: (contributor) => (0, AuthorsModal_1.handleSaveContributor)(this.view, contributor, this.getPos()),
226
- onDeleteAuthor: (contributor) => (0, AuthorsModal_1.handleDeleteContributor)(this.view, contributor),
227
224
  addNewAuthor: addNew,
228
225
  };
229
226
  this.popper?.remove();
230
- this.popper = (0, ReactSubView_1.default)(this.props, AuthorsModal_1.AuthorsModal, componentProps, this.node, this.getPos, this.view);
227
+ this.popper = (0, ReactSubView_1.default)(this.props, AuthorsAndAffiliationsModals_1.AuthorsAndAffiliationsModals, componentProps, this.node, this.getPos, this.view);
231
228
  this.container.appendChild(this.popper);
232
229
  };
233
230
  }
@@ -33,8 +33,7 @@ import { getEditorProps } from './plugins/editor-props';
33
33
  import { searchReplaceKey } from './plugins/search-replace';
34
34
  import { checkForCompletion } from './plugins/section_title/autocompletion';
35
35
  import { persistentCursor } from './plugins/persistent-cursor';
36
- import { openAffiliationsModal } from './components/affiliations/AffiliationsModal';
37
- import { openAuthorsModal } from './components/authors/AuthorsModal';
36
+ import { openAuthorsAndAffiliationsModals } from './components/authors-affiliations/AuthorsAndAffiliationsModals';
38
37
  export const addToStart = (state, dispatch) => {
39
38
  const { selection } = state;
40
39
  const props = getEditorProps(state);
@@ -713,7 +712,7 @@ export const insertContributors = (state, dispatch, view) => {
713
712
  if (dispatch) {
714
713
  const selection = NodeSelection.create(tr.doc, contributors.pos);
715
714
  dispatch(tr.setSelection(selection).scrollIntoView());
716
- openAuthorsModal(contributors.pos, view);
715
+ openAuthorsAndAffiliationsModals(contributors.pos, view, 'authors');
717
716
  }
718
717
  return true;
719
718
  };
@@ -734,7 +733,7 @@ export const insertAffiliation = (state, dispatch, view) => {
734
733
  if (dispatch) {
735
734
  const selection = NodeSelection.create(tr.doc, affiliations.pos);
736
735
  dispatch(tr.setSelection(selection).scrollIntoView());
737
- openAffiliationsModal(affiliations.pos, view);
736
+ openAuthorsAndAffiliationsModals(affiliations.pos, view, 'affiliations');
738
737
  }
739
738
  return true;
740
739
  };
@@ -3,14 +3,13 @@ import { Field, Formik } from 'formik';
3
3
  import React, { useRef } from 'react';
4
4
  import { ChangeHandlingForm } from '../ChangeHandlingForm';
5
5
  export const AffiliationForm = ({ values, onSave, onChange, actionsRef, }) => {
6
- if (actionsRef && !actionsRef.current) {
6
+ const formRef = useRef(null);
7
+ if (actionsRef) {
7
8
  actionsRef.current = {
8
- reset: () => {
9
- formRef.current?.resetForm();
10
- },
9
+ reset: () => formRef.current?.resetForm(),
10
+ submitForm: () => formRef.current?.submitForm(),
11
11
  };
12
12
  }
13
- const formRef = useRef(null);
14
13
  const validateAffiliation = (values) => {
15
14
  const errors = {};
16
15
  if (!values.institution?.trim()) {
@@ -18,7 +17,7 @@ export const AffiliationForm = ({ values, onSave, onChange, actionsRef, }) => {
18
17
  }
19
18
  return errors;
20
19
  };
21
- return (React.createElement(Formik, { initialValues: values, onSubmit: onSave, innerRef: formRef, enableReinitialize: true, validate: validateAffiliation }, (formik) => (React.createElement(ChangeHandlingForm, { onChange: onChange, id: "affiliation-form", noValidate: true },
20
+ return (React.createElement(Formik, { initialValues: values, onSubmit: (attrs) => onSave(attrs), innerRef: formRef, enableReinitialize: true, validate: validateAffiliation }, (formik) => (React.createElement(ChangeHandlingForm, { onChange: onChange, id: "affiliation-form", noValidate: true },
22
21
  React.createElement(FormRow, null,
23
22
  React.createElement(Field, { name: "institution" }, (props) => {
24
23
  const hasError = formik.touched.institution && formik.errors.institution;
@@ -1,4 +1,4 @@
1
- import { AddIcon, AddUserIcon, AffiliationPlaceholderIcon, CloseButton, InspectorTab, InspectorTabList, InspectorTabPanel, InspectorTabPanels, InspectorTabs, outlineStyle, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, ScrollableModalContent, SidebarContent, StyledModal, } from '@manuscripts/style-guide';
1
+ import { AddIcon, AffiliationPlaceholderIcon, CloseButton, InspectorTab, InspectorTabList, InspectorTabPanel, InspectorTabPanels, InspectorTabs, outlineStyle, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, ScrollableModalContent, SidebarContent, StyledModal, } from '@manuscripts/style-guide';
2
2
  import { generateNodeID, schema } from '@manuscripts/transform';
3
3
  import { isEqual } from 'lodash';
4
4
  import React, { useCallback, useEffect, useReducer, useRef, useState, } from 'react';
@@ -11,37 +11,45 @@ import { ConfirmationDialog, DialogType } from '../dialog/ConfirmationDialog';
11
11
  import FormFooter from '../form/FormFooter';
12
12
  import { FormPlaceholder } from '../form/FormPlaceholder';
13
13
  import { ModalFormActions } from '../form/ModalFormActions';
14
- import { GenericDrawer } from '../modal-drawer/GenericDrawer';
15
- import { DrawerGroup } from '../modal-drawer/GenericDrawerGroup';
14
+ import { AuthorsPanel } from '../authors/AuthorsPanel';
16
15
  import { AffiliationForm } from './AffiliationForm';
17
16
  import { AffiliationList } from './AffiliationList';
18
- import { getEditorProps } from '../../plugins/editor-props';
19
- import ReactSubView from '../../views/ReactSubView';
20
- import { deleteNode, findChildrenAttrsByType, updateNodeAttrs, } from '../../lib/view';
21
17
  function makeAuthorItems(authors) {
22
18
  return authors.map((author) => ({
23
19
  id: author.id,
24
20
  label: `${author.given} ${author.family}`,
25
21
  }));
26
22
  }
27
- export const AffiliationsModal = ({ authors: $authors, affiliations: $affiliations, affiliation, onSaveAffiliation, onDeleteAffiliation, onUpdateAuthors, addNewAffiliation = false, }) => {
23
+ export const AffiliationsModal = ({ authors: $authors, affiliations: $affiliations, affiliation, onSaveAffiliation, onDeleteAffiliation, onUpdateAuthors, addNewAffiliation = false, onClose, onOpenAuthorsModal, }) => {
28
24
  const [isOpen, setIsOpen] = useState(true);
29
25
  const [selection, setSelection] = useState(affiliation);
30
26
  const [showingDeleteDialog, setShowDeleteDialog] = useState(false);
31
27
  const valuesRef = useRef(undefined);
32
28
  const actionsRef = useRef(undefined);
33
29
  const [authors, dispatchAuthors] = useReducer(authorsReducer, $authors.sort(authorComparator));
30
+ useEffect(() => {
31
+ dispatchAuthors({
32
+ type: 'set',
33
+ state: [...$authors].sort(authorComparator),
34
+ });
35
+ }, [$authors]);
34
36
  const [affiliations, dispatchAffiliations] = useReducer(affiliationsReducer, $affiliations);
35
37
  const [isDisableSave, setIsDisableSave] = useState(true);
36
38
  const [newAffiliation, setNewAffiliation] = useState(false);
37
39
  const [showRequiredFieldConfirmationDialog, setShowRequiredFieldConfirmationDialog,] = useState(false);
38
40
  const [showConfirmationDialog, setShowConfirmationDialog] = useState(false);
39
- const [showAuthorDrawer, setShowAuthorDrawer] = useState(false);
40
41
  const [selectedAuthorIds, setSelectedAuthorIds] = useState([]);
41
42
  const [pendingSelection, setPendingSelection] = useState(null);
42
43
  const [pendingAction, setPendingAction] = useState(null);
43
44
  const [savedAffiliationId, setSavedAffiliationId] = useState(undefined);
44
45
  const [affiliationAuthorMap, setAffiliationAuthorMap] = useState(new Map());
46
+ const prevIsOpenRef = useRef(isOpen);
47
+ useEffect(() => {
48
+ if (prevIsOpenRef.current && !isOpen) {
49
+ onClose?.();
50
+ }
51
+ prevIsOpenRef.current = isOpen;
52
+ }, [isOpen, onClose]);
45
53
  useEffect(() => {
46
54
  if (!selection) {
47
55
  return;
@@ -106,7 +114,6 @@ export const AffiliationsModal = ({ authors: $authors, affiliations: $affiliatio
106
114
  setNewAffiliation(false);
107
115
  setSelection(affiliation);
108
116
  setSelectedAuthorIds(affiliatedAuthorIds);
109
- setShowAuthorDrawer(false);
110
117
  setAffiliationAuthorMap((prevMap) => {
111
118
  const newMap = new Map(prevMap);
112
119
  newMap.set(affiliation.id, affiliatedAuthorIds);
@@ -146,7 +153,6 @@ export const AffiliationsModal = ({ authors: $authors, affiliations: $affiliatio
146
153
  newMap.set(affiliation.id, selectedAuthorIds);
147
154
  return newMap;
148
155
  });
149
- setShowAuthorDrawer(false);
150
156
  setSavedAffiliationId(affiliation.id);
151
157
  setTimeout(() => {
152
158
  setSavedAffiliationId(undefined);
@@ -240,7 +246,6 @@ export const AffiliationsModal = ({ authors: $authors, affiliations: $affiliatio
240
246
  setNewAffiliation(true);
241
247
  setSelection(emptyAffiliation);
242
248
  setSelectedAuthorIds([]);
243
- setShowAuthorDrawer(false);
244
249
  };
245
250
  useEffect(() => {
246
251
  if (addNewAffiliation) {
@@ -288,7 +293,6 @@ export const AffiliationsModal = ({ authors: $authors, affiliations: $affiliatio
288
293
  const handleConfirmationCancel = () => {
289
294
  setShowConfirmationDialog(false);
290
295
  setShowRequiredFieldConfirmationDialog(false);
291
- setShowAuthorDrawer(false);
292
296
  if (pendingAction === 'select' && pendingSelection) {
293
297
  setSelection(pendingSelection);
294
298
  setNewAffiliation(false);
@@ -329,19 +333,17 @@ export const AffiliationsModal = ({ authors: $authors, affiliations: $affiliatio
329
333
  React.createElement(AffiliationList, { affiliation: selection, affiliations: affiliations, onSelect: handleSelect, onDelete: handleShowDeleteDialog, lastSavedAffiliationId: savedAffiliationId }))),
330
334
  React.createElement(ScrollableModalContent, { "data-cy": "affiliations-modal-content" }, selection ? (React.createElement(React.Fragment, null,
331
335
  React.createElement(AffiliationTabs, null,
332
- React.createElement(ModalFormActions, { type: 'affiliation', form: 'affiliation-form', onDelete: handleDeleteAffiliation, showingDeleteDialog: showingDeleteDialog &&
336
+ React.createElement(ModalFormActions, { type: 'affiliation', form: 'affiliation-form', onSubmitForm: () => actionsRef.current?.submitForm?.(), onDelete: handleDeleteAffiliation, showingDeleteDialog: showingDeleteDialog &&
333
337
  !(showConfirmationDialog ||
334
338
  showRequiredFieldConfirmationDialog), showDeleteDialog: handleShowDeleteDialog, newEntity: newAffiliation, isDisableSave: isDisableSave }),
335
339
  React.createElement(InspectorTabList, null,
336
340
  React.createElement(InspectorTab, null, "Details"),
337
- React.createElement(InspectorTab, null, "Authors")),
341
+ onOpenAuthorsModal && React.createElement(InspectorTab, null, "Authors")),
338
342
  React.createElement(InspectorTabPanels, null,
339
343
  React.createElement(AffiliationTabPanel, null,
340
- React.createElement(AffiliationForm, { values: checkID(selection, 'affiliation'), onSave: () => handleSaveAffiliation(valuesRef.current), onChange: handleAffiliationChange, actionsRef: actionsRef })),
341
- React.createElement(AffiliationTabPanel, null,
342
- React.createElement(DrawerGroup, { Drawer: GenericDrawer, removeItem: (id) => {
343
- setSelectedAuthorIds((prev) => prev.filter((authorId) => authorId !== id));
344
- }, selectedItems: selectedAuthors, onSelect: selectAuthor, items: makeAuthorItems(authors), showDrawer: showAuthorDrawer, setShowDrawer: setShowAuthorDrawer, title: "Authors", cy: "affiliations", labelField: "label", buttonText: "Affiliate Authors", Icon: React.createElement(AddUserIcon, { width: 16, height: 16 }) })))),
344
+ React.createElement(AffiliationForm, { values: checkID(selection, 'affiliation'), onSave: (attrs) => handleSaveAffiliation(attrs), onChange: handleAffiliationChange, actionsRef: actionsRef })),
345
+ onOpenAuthorsModal && (React.createElement(AffiliationTabPanel, null,
346
+ React.createElement(AuthorsPanel, { items: makeAuthorItems(authors), selectedItems: selectedAuthors, onSelect: selectAuthor, onOpenAuthorsModal: onOpenAuthorsModal }))))),
345
347
  React.createElement(ConfirmationDialog, { isOpen: showRequiredFieldConfirmationDialog, onPrimary: () => setShowRequiredFieldConfirmationDialog(false), onSecondary: handleConfirmationCancel, type: DialogType.REQUIRED, entityType: "affiliation" }),
346
348
  React.createElement(ConfirmationDialog, { isOpen: showConfirmationDialog, onPrimary: handleConfirmationSave, onSecondary: handleConfirmationCancel, type: DialogType.SAVE, entityType: "affiliation" }))) : (React.createElement(FormPlaceholder, { type: "affiliation", title: "Affiliation Details", message: "Select an affiliation from the list to display it's details here.", placeholderIcon: React.createElement(AffiliationPlaceholderIcon, null) })))),
347
349
  React.createElement(FormFooter, { onCancel: handleClose }))));
@@ -407,37 +409,3 @@ const StyledModalSidebarHeader = styled(ModalSidebarHeader) `
407
409
  margin-top: 8px;
408
410
  margin-bottom: 16px;
409
411
  `;
410
- export const openAffiliationsModal = (pos, view) => {
411
- if (!view) {
412
- return;
413
- }
414
- const { state } = view;
415
- const props = getEditorProps(state);
416
- const contributors = findChildrenAttrsByType(view, schema.nodes.contributor);
417
- const componentProps = {
418
- affiliations: [],
419
- authors: contributors,
420
- onSaveAffiliation: (affiliation) => handleSaveAffiliation(view, affiliation, pos),
421
- onDeleteAffiliation: (affiliation) => handleDeleteAffiliation(view, affiliation),
422
- onUpdateAuthors: (authors) => handleUpdateAuthors(view, authors),
423
- addNewAffiliation: true,
424
- };
425
- const dialog = ReactSubView(props, AffiliationsModal, componentProps, state.doc, () => pos, view);
426
- view.focus();
427
- document.body.appendChild(dialog);
428
- };
429
- export const handleSaveAffiliation = (view, affiliation, affiliationsPos) => {
430
- const update = updateNodeAttrs(view, schema.nodes.affiliation, affiliation);
431
- if (!update) {
432
- const node = schema.nodes.affiliation.create(affiliation);
433
- view.dispatch(view.state.tr.insert(affiliationsPos + 1, node));
434
- }
435
- };
436
- export const handleDeleteAffiliation = (view, affiliation) => {
437
- deleteNode(view, affiliation.id);
438
- };
439
- export const handleUpdateAuthors = (view, authors) => {
440
- authors.forEach((author) => {
441
- updateNodeAttrs(view, schema.nodes.contributor, author);
442
- });
443
- };
@@ -0,0 +1,15 @@
1
+ import { AffiliationPlaceholderIcon } from '@manuscripts/style-guide';
2
+ import React from 'react';
3
+ import { GenericPanel, ListItem, ListItems, useListSelectedIds, } from '../authors-affiliations/GenericPanel';
4
+ function affiliationSecondaryLine(item) {
5
+ const line = [item.city, item.county, item.country].filter(Boolean).join(', ');
6
+ return line || undefined;
7
+ }
8
+ export const AffiliationsPanel = ({ items, selectedItems = [], onSelect, onOpenAffiliationsModal, }) => {
9
+ const selectedIds = useListSelectedIds(selectedItems);
10
+ return (React.createElement(GenericPanel, { title: "Institutional Affiliations", createLabel: "Create New Affiliation", onCreate: onOpenAffiliationsModal, createDataCy: "add-affiliations-link", emptyDataCy: "affiliations-panel-empty", isEmpty: items.length === 0, emptyIcon: React.createElement(AffiliationPlaceholderIcon, null), emptyMessage: React.createElement(React.Fragment, null,
11
+ "There are no affiliations attributed yet!",
12
+ React.createElement("br", null),
13
+ "Click \u2018Create New Affiliation\u2019") },
14
+ React.createElement(ListItems, { "data-cy": "affiliations-panel" }, items.map((item) => (React.createElement(ListItem, { key: item.id, selected: selectedIds.has(item.id), onClick: () => onSelect(item.id), primary: item.institution, secondary: affiliationSecondaryLine(item) }))))));
15
+ };
@@ -34,11 +34,10 @@ export const AuthorDetailsForm = ({ values, onChange, onSave, actionsRef, isEmai
34
34
  formRef.current.setFieldValue('creditRoles', selectedCreditRoles);
35
35
  }
36
36
  }, [selectedCreditRoles]);
37
- if (actionsRef && !actionsRef.current) {
37
+ if (actionsRef) {
38
38
  actionsRef.current = {
39
- reset: () => {
40
- formRef.current?.resetForm();
41
- },
39
+ reset: () => formRef.current?.resetForm(),
40
+ submitForm: () => formRef.current?.submitForm(),
42
41
  };
43
42
  }
44
43
  const validateAuthor = (values) => {
@@ -13,7 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { AddIcon, AddInstitutionIcon, AddRoleIcon, AuthorPlaceholderIcon, CloseButton, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, outlineStyle, ScrollableModalContent, SidebarContent, StyledModal, InspectorTabs, InspectorTabPanel, InspectorTabList, InspectorTab, InspectorTabPanels, } from '@manuscripts/style-guide';
16
+ import { AddIcon, AddRoleIcon, AuthorPlaceholderIcon, CloseButton, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, outlineStyle, ScrollableModalContent, SidebarContent, StyledModal, InspectorTabs, InspectorTabPanel, InspectorTabList, InspectorTab, InspectorTabPanels, } from '@manuscripts/style-guide';
17
17
  import { generateNodeID, schema } from '@manuscripts/transform';
18
18
  import { cloneDeep, isEqual, omit } from 'lodash';
19
19
  import React, { useCallback, useEffect, useReducer, useRef, useState, } from 'react';
@@ -26,18 +26,22 @@ import FormFooter from '../form/FormFooter';
26
26
  import { FormPlaceholder } from '../form/FormPlaceholder';
27
27
  import { ModalFormActions } from '../form/ModalFormActions';
28
28
  import { DrawerGroup } from '../modal-drawer/GenericDrawerGroup';
29
- import { AffiliationsDrawer } from './AffiliationDrawer';
29
+ import { AffiliationsPanel } from '../affiliations/AffiliationsPanel';
30
30
  import { AuthorDetailsForm } from './AuthorDetailsForm';
31
31
  import { AuthorList } from './AuthorList';
32
32
  import { CreditDrawer } from './CreditDrawer';
33
33
  import { useManageAffiliations } from './useManageAffiliations';
34
34
  import { useManageCredit } from './useManageCredit';
35
- import { getEditorProps } from '../../plugins/editor-props';
36
- import ReactSubView from '../../views/ReactSubView';
37
- import { deleteNode, findChildrenAttrsByType, updateNodeAttrs, } from '../../lib/view';
38
35
  export const authorsReducer = arrayReducer((a, b) => a.id === b.id);
39
- export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, author, onSaveAuthor, onDeleteAuthor, addNewAuthor = false, }) => {
36
+ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, author, onSaveAuthor, onDeleteAuthor, addNewAuthor = false, onOpenAffiliationsModal, onClose, }) => {
40
37
  const [isOpen, setOpen] = useState(true);
38
+ const prevIsOpenRef = useRef(true);
39
+ useEffect(() => {
40
+ if (prevIsOpenRef.current && !isOpen) {
41
+ onClose?.();
42
+ }
43
+ prevIsOpenRef.current = isOpen;
44
+ }, [isOpen, onClose]);
41
45
  const [isDisableSave, setDisableSave] = useState(true);
42
46
  const [isEmailRequired, setEmailRequired] = useState(false);
43
47
  const [showConfirmationDialog, setShowConfirmationDialog] = useState(false);
@@ -60,7 +64,7 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
60
64
  }
61
65
  }, [addNewAuthor]);
62
66
  const [selection, setSelection] = useState(author);
63
- const { showAffiliationDrawer, setShowAffiliationDrawer, selectedAffiliations, setSelectedAffiliations, removeAffiliation, selectAffiliation, affiliations, } = useManageAffiliations(selection, $affiliations);
67
+ const { selectedAffiliations, setSelectedAffiliations, selectAffiliation, affiliations, } = useManageAffiliations(selection, $affiliations);
64
68
  useEffect(() => {
65
69
  const currentAuthor = selection;
66
70
  const relevantAffiliations = affiliations.filter((item) => currentAuthor?.affiliationIDs?.includes(item.id));
@@ -87,12 +91,10 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
87
91
  else {
88
92
  updateAffiliationSelection(author);
89
93
  setSelection(author);
90
- setShowAffiliationDrawer(false);
91
94
  setNewAuthor(false);
92
95
  }
93
96
  }
94
97
  else {
95
- setShowAffiliationDrawer(false);
96
98
  updateAffiliationSelection(author);
97
99
  setSelection(author);
98
100
  setNewAuthor(false);
@@ -133,7 +135,6 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
133
135
  setSelection(nextAuthor);
134
136
  setNextAuthor(null);
135
137
  setNewAuthor(false);
136
- setShowAffiliationDrawer(false);
137
138
  updateAffiliationSelection(nextAuthor);
138
139
  setIsCreatingNewAuthor(false);
139
140
  }
@@ -166,7 +167,6 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
166
167
  }
167
168
  setShowConfirmationDialog(false);
168
169
  setShowRequiredFieldConfirmationDialog(false);
169
- setShowAffiliationDrawer(false);
170
170
  };
171
171
  const saveAuthor = (values) => {
172
172
  if (!values || !selection) {
@@ -185,7 +185,6 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
185
185
  setSelection(author);
186
186
  setShowConfirmationDialog(false);
187
187
  setNewAuthor(false);
188
- setShowAffiliationDrawer(false);
189
188
  setIsCreatingNewAuthor(false);
190
189
  dispatchAuthors({
191
190
  type: 'update',
@@ -242,7 +241,6 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
242
241
  }
243
242
  else {
244
243
  createNewAuthor();
245
- setShowAffiliationDrawer(false);
246
244
  }
247
245
  };
248
246
  const deleteAuthor = () => {
@@ -311,19 +309,19 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
311
309
  React.createElement(AuthorList, { author: selection, authors: authors, onSelect: selectAuthor, onDelete: () => setShowDeleteDialog((prev) => !prev), moveAuthor: moveAuthor, lastSavedAuthor: lastSavedAuthor }))),
312
310
  React.createElement(ScrollableModalContent, { "data-cy": "author-modal-content" }, selection ? (React.createElement(React.Fragment, null,
313
311
  React.createElement(AuthorTabs, null,
314
- React.createElement(ModalFormActions, { form: 'author-details-form', type: "author", onDelete: deleteAuthor, showingDeleteDialog: showingDeleteDialog, showDeleteDialog: () => setShowDeleteDialog((prev) => !prev), newEntity: newAuthor ||
312
+ React.createElement(ModalFormActions, { form: 'author-details-form', onSubmitForm: () => actionsRef.current?.submitForm?.(), type: "author", onDelete: deleteAuthor, showingDeleteDialog: showingDeleteDialog, showDeleteDialog: () => setShowDeleteDialog((prev) => !prev), newEntity: newAuthor ||
315
313
  (isCreatingNewAuthor &&
316
314
  !showConfirmationDialog &&
317
315
  !showRequiredFieldConfirmationDialog), isDisableSave: isDisableSave }),
318
316
  React.createElement(InspectorTabList, null,
319
317
  React.createElement(InspectorTab, null, "Details"),
320
- React.createElement(InspectorTab, null, "Affiliations"),
318
+ onOpenAffiliationsModal && (React.createElement(InspectorTab, null, "Affiliations")),
321
319
  React.createElement(InspectorTab, null, "Contributions (CRediT)")),
322
320
  React.createElement(InspectorTabPanels, null,
323
321
  React.createElement(AuthorTabPanel, null,
324
322
  React.createElement(AuthorDetailsForm, { values: normalizeAuthor(selection), onChange: changeAuthor, onSave: saveAuthor, actionsRef: actionsRef, isEmailRequired: isEmailRequired, selectedAffiliations: selectedAffiliations.map((a) => a.id), authorFormRef: authorFormRef, selectedCreditRoles: selectedCreditRoles })),
325
- React.createElement(AuthorTabPanel, null,
326
- 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 }) })),
323
+ onOpenAffiliationsModal && (React.createElement(AuthorTabPanel, null,
324
+ React.createElement(AffiliationsPanel, { items: affiliations, selectedItems: selectedAffiliations, onSelect: selectAffiliation, onOpenAffiliationsModal: onOpenAffiliationsModal }))),
327
325
  React.createElement(AuthorTabPanel, null,
328
326
  React.createElement(DrawerGroup, { Drawer: CreditDrawer, removeItem: removeCreditRole, selectedItems: selectedCreditRoles.map((r) => ({
329
327
  id: r.vocabTerm,
@@ -391,31 +389,3 @@ const StyledModalBody = styled(ModalBody) `
391
389
  const StyledModalSidebarHeader = styled(ModalSidebarHeader) `
392
390
  margin-bottom: 16px;
393
391
  `;
394
- export const openAuthorsModal = (pos, view) => {
395
- if (!view) {
396
- return;
397
- }
398
- const { state } = view;
399
- const props = getEditorProps(state);
400
- const affiliations = findChildrenAttrsByType(view, schema.nodes.affiliation);
401
- const componentProps = {
402
- authors: [],
403
- affiliations,
404
- onSaveAuthor: (contributor) => handleSaveContributor(view, contributor, pos),
405
- onDeleteAuthor: (contributor) => handleDeleteContributor(view, contributor),
406
- addNewAuthor: true,
407
- };
408
- const dialog = ReactSubView(props, AuthorsModal, componentProps, state.doc, () => pos, view);
409
- view.focus();
410
- document.body.appendChild(dialog);
411
- };
412
- export const handleSaveContributor = (view, contributor, contributorsPos) => {
413
- const update = updateNodeAttrs(view, schema.nodes.contributor, contributor);
414
- if (!update) {
415
- const node = schema.nodes.contributor.create(contributor);
416
- view.dispatch(view.state.tr.insert(contributorsPos + 1, node));
417
- }
418
- };
419
- export const handleDeleteContributor = (view, contributor) => {
420
- deleteNode(view, contributor.id);
421
- };