@manuscripts/body-editor 3.16.1 → 3.16.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 (50) hide show
  1. package/dist/cjs/components/references/CitationEditor.js +13 -19
  2. package/dist/cjs/components/references/ImportBibliography.js +77 -0
  3. package/dist/cjs/components/references/ImportBibliographyForm.js +1 -1
  4. package/dist/cjs/components/references/ImportBibliographyModal.js +12 -81
  5. package/dist/cjs/components/references/ImportReferencesConfirmation.js +113 -0
  6. package/dist/cjs/components/references/ImportSuccessPlaceholder.js +56 -0
  7. package/dist/cjs/components/references/ReferenceSearch.js +2 -5
  8. package/dist/cjs/components/references/ReferencesEditor.js +18 -2
  9. package/dist/cjs/components/references/ReferencesModal.js +86 -33
  10. package/dist/cjs/lib/view.js +39 -1
  11. package/dist/cjs/versions.js +1 -1
  12. package/dist/cjs/views/bibliography_element.js +1 -2
  13. package/dist/cjs/views/citation_editable.js +2 -29
  14. package/dist/es/components/references/CitationEditor.js +13 -19
  15. package/dist/es/components/references/ImportBibliography.js +70 -0
  16. package/dist/es/components/references/ImportBibliographyForm.js +1 -1
  17. package/dist/es/components/references/ImportBibliographyModal.js +13 -79
  18. package/dist/es/components/references/ImportReferencesConfirmation.js +73 -0
  19. package/dist/es/components/references/ImportSuccessPlaceholder.js +49 -0
  20. package/dist/es/components/references/ReferenceSearch.js +3 -6
  21. package/dist/es/components/references/ReferencesEditor.js +19 -3
  22. package/dist/es/components/references/ReferencesModal.js +88 -35
  23. package/dist/es/lib/view.js +36 -0
  24. package/dist/es/versions.js +1 -1
  25. package/dist/es/views/bibliography_element.js +2 -3
  26. package/dist/es/views/citation_editable.js +3 -30
  27. package/dist/types/components/references/CitationEditor.d.ts +1 -1
  28. package/dist/types/components/references/ImportBibliography.d.ts +7 -0
  29. package/dist/types/components/references/ImportReferencesConfirmation.d.ts +8 -0
  30. package/dist/types/components/references/ImportSuccessPlaceholder.d.ts +5 -0
  31. package/dist/types/components/references/ReferenceSearch.d.ts +0 -1
  32. package/dist/types/components/references/ReferencesEditor.d.ts +4 -1
  33. package/dist/types/components/references/ReferencesModal.d.ts +1 -0
  34. package/dist/types/lib/view.d.ts +3 -1
  35. package/dist/types/versions.d.ts +1 -1
  36. package/dist/types/views/citation_editable.d.ts +0 -1
  37. package/package.json +1 -1
  38. package/src/components/references/CitationEditor.tsx +16 -25
  39. package/src/components/references/ImportBibliography.tsx +119 -0
  40. package/src/components/references/ImportBibliographyForm.tsx +1 -1
  41. package/src/components/references/ImportBibliographyModal.tsx +34 -110
  42. package/src/components/references/ImportReferencesConfirmation.tsx +133 -0
  43. package/src/components/references/ImportSuccessPlaceholder.tsx +93 -0
  44. package/src/components/references/ReferenceSearch.tsx +0 -7
  45. package/src/components/references/ReferencesEditor.tsx +35 -6
  46. package/src/components/references/ReferencesModal.tsx +164 -78
  47. package/src/lib/view.ts +65 -0
  48. package/src/versions.ts +1 -1
  49. package/src/views/bibliography_element.ts +3 -4
  50. package/src/views/citation_editable.ts +5 -50
@@ -0,0 +1,73 @@
1
+ import { CheckboxField, CheckboxLabel, FormActionsBar, ModalTitle, PrimaryButton, SecondaryButton, } from '@manuscripts/style-guide';
2
+ import React, { useState } from 'react';
3
+ import styled from 'styled-components';
4
+ import { ReferenceLine } from './ReferenceLine';
5
+ export const ImportReferencesConfirmation = ({ items, onCancel, onConfirm }) => {
6
+ const [selected, setSelected] = useState(() => items.map(() => true));
7
+ const selectedCount = selected.filter(Boolean).length;
8
+ const countLabel = selectedCount === 1 ? 'reference' : 'references';
9
+ const toggleItem = (index) => {
10
+ setSelected((current) => current.map((value, i) => (i === index ? !value : value)));
11
+ };
12
+ const handleConfirm = () => {
13
+ onConfirm(items.filter((_, index) => selected[index]));
14
+ };
15
+ return (React.createElement(React.Fragment, null,
16
+ React.createElement(Title, null, "Import References"),
17
+ React.createElement(Subtitle, null,
18
+ "Importing ",
19
+ selectedCount,
20
+ " ",
21
+ countLabel),
22
+ React.createElement(List, null, items.map((item, index) => (React.createElement(ListItem, { key: index },
23
+ React.createElement(ItemCheckbox, null,
24
+ React.createElement(CheckboxField, { checked: selected[index], "aria-label": `Select reference ${index + 1}`, onChange: () => toggleItem(index) }),
25
+ React.createElement("div", null)),
26
+ React.createElement(ReferenceLine, { item: item }))))),
27
+ React.createElement(FormActionsBar, null,
28
+ React.createElement(SecondaryButton, { type: "button", onClick: onCancel }, "Cancel"),
29
+ React.createElement(PrimaryButton, { type: "button", disabled: selectedCount === 0, onClick: handleConfirm },
30
+ "Import ",
31
+ selectedCount,
32
+ " ",
33
+ countLabel))));
34
+ };
35
+ const Title = styled(ModalTitle) `
36
+ border-bottom: 1px solid ${(props) => props.theme.colors.border.secondary};
37
+ margin: 0 -${(props) => 6 * props.theme.grid.unit}px 20px;
38
+ padding: 0 ${(props) => 6 * props.theme.grid.unit}px
39
+ ${(props) => props.theme.grid.unit * 4}px;
40
+ `;
41
+ const Subtitle = styled.p `
42
+ color: ${(props) => props.theme.colors.text.secondary};
43
+ font-weight: ${(props) => props.theme.font.weight.bold};
44
+ margin: 0 0 16px;
45
+ font-size: 14px;
46
+ `;
47
+ const List = styled.div `
48
+ display: flex;
49
+ flex-direction: column;
50
+ margin-bottom: ${(props) => props.theme.grid.unit * 5}px;
51
+ overflow-y: auto;
52
+ `;
53
+ const ListItem = styled.div `
54
+ border-bottom: 1px solid ${(props) => props.theme.colors.border.secondary};
55
+ display: flex;
56
+ align-items: flex-start;
57
+ gap: ${(props) => props.theme.grid.unit * 3}px;
58
+ padding: ${(props) => props.theme.grid.unit * 3}px 0;
59
+ `;
60
+ const ItemCheckbox = styled(CheckboxLabel) `
61
+ flex-shrink: 0;
62
+
63
+ > div {
64
+ margin: 0;
65
+
66
+ &::before {
67
+ width: 18px;
68
+ height: 18px;
69
+ border-radius: 4px;
70
+ margin: 0;
71
+ }
72
+ }
73
+ `;
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ export const ImportSuccessPlaceholder = ({ count }) => {
4
+ const countLabel = count === 1 ? 'reference' : 'references';
5
+ return (React.createElement(Container, { "data-cy": "import-success-placeholder" },
6
+ React.createElement(IconCircle, null,
7
+ React.createElement(SuccessCheckIcon, null)),
8
+ React.createElement(Title, null, "Successfully Imported"),
9
+ React.createElement(Message, null,
10
+ "Your library has been updated with ",
11
+ count,
12
+ " new ",
13
+ countLabel,
14
+ ".")));
15
+ };
16
+ const SuccessCheckIcon = () => (React.createElement("svg", { width: "32", height: "32", viewBox: "0 0 32 32", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true" },
17
+ React.createElement("path", { d: "M26.6656 8L12.0004 22.6656L5.3344 15.9994", stroke: "#36B260", strokeWidth: "3", strokeLinecap: "round" })));
18
+ const Container = styled.div `
19
+ display: flex;
20
+ flex-direction: column;
21
+ align-items: center;
22
+ justify-content: center;
23
+ gap: ${(props) => props.theme.grid.unit * 4}px;
24
+ height: 100%;
25
+ padding: 40px;
26
+ box-sizing: border-box;
27
+ text-align: center;
28
+ `;
29
+ const IconCircle = styled.div `
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ width: 64px;
34
+ height: 64px;
35
+ border-radius: 32px;
36
+ background: #f6ffed;
37
+ `;
38
+ const Title = styled.h2 `
39
+ margin: 0;
40
+ font-size: 24px;
41
+ line-height: 32px;
42
+ letter-spacing: -0.37px;
43
+ color: ${(props) => props.theme.colors.text.primary};
44
+ `;
45
+ const Message = styled.p `
46
+ margin: 0;
47
+ line-height: ${(props) => props.theme.font.lineHeight.large};
48
+ color: ${(props) => props.theme.colors.text.secondary};
49
+ `;
@@ -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 { AddNewIcon, ButtonGroup, IconTextButton, PrimaryButton, SecondaryButton, UploadIcon, withFocusTrap, } from '@manuscripts/style-guide';
16
+ import { AddNewIcon, ButtonGroup, IconTextButton, PrimaryButton, SecondaryButton, withFocusTrap, } from '@manuscripts/style-guide';
17
17
  import debounce from 'lodash/debounce';
18
18
  import React, { useState } from 'react';
19
19
  import styled from 'styled-components';
@@ -50,7 +50,7 @@ const AddReferenceActions = styled(ButtonGroup) `
50
50
  }
51
51
  }
52
52
  `;
53
- export const ReferenceSearch = ({ query: initialQuery, sources, items, onAdd, onImport, onCite, onCancel, }) => {
53
+ export const ReferenceSearch = ({ query: initialQuery, sources, items, onAdd, onCite, onCancel, }) => {
54
54
  const [query, setQuery] = useState(initialQuery || '');
55
55
  const [selections, setSelections] = useState(new Map());
56
56
  const toggleSelection = (item) => {
@@ -86,10 +86,7 @@ export const ReferenceSearch = ({ query: initialQuery, sources, items, onAdd, on
86
86
  React.createElement(AddReferenceActions, null,
87
87
  React.createElement(IconTextButton, { onClick: onAdd },
88
88
  React.createElement(AddNewIcon, null),
89
- "Add new"),
90
- React.createElement(IconTextButton, { onClick: onImport },
91
- React.createElement(UploadIcon, null),
92
- "Import new")),
89
+ "Add new")),
93
90
  React.createElement(ButtonGroup, null,
94
91
  React.createElement(SecondaryButton, { onClick: onCancel }, "Close"),
95
92
  React.createElement(PrimaryButton, { onClick: handleClick, disabled: selections.size === 0 }, "Cite")))));
@@ -1,4 +1,5 @@
1
- import React, { useReducer, useState } from 'react';
1
+ import { generateNodeID, schema, } from '@manuscripts/transform';
2
+ import React, { useEffect, useReducer, useState } from 'react';
2
3
  import { attrsReducer } from '../../lib/array-reducer';
3
4
  import { cleanItemValues } from '../../lib/utils';
4
5
  import { ReferencesModal } from './ReferencesModal';
@@ -6,9 +7,13 @@ const itemsReducer = attrsReducer();
6
7
  export const ReferencesEditor = (props) => {
7
8
  const [isOpen, setOpen] = useState(true);
8
9
  const [items, dispatch] = useReducer(itemsReducer, props.items);
10
+ const [selectedItem, setSelectedItem] = useState(props.item);
11
+ useEffect(() => {
12
+ setSelectedItem(props.item);
13
+ }, [props.item]);
9
14
  const handleSave = (item) => {
10
15
  const cleanedItem = cleanItemValues(item);
11
- props.onSave(cleanedItem);
16
+ props.onSave([cleanedItem]);
12
17
  dispatch({
13
18
  type: 'update',
14
19
  items: [cleanedItem],
@@ -21,5 +26,16 @@ export const ReferencesEditor = (props) => {
21
26
  item: item,
22
27
  });
23
28
  };
24
- return (React.createElement(ReferencesModal, { ...props, isOpen: isOpen, onCancel: () => setOpen(false), items: items, onSave: handleSave, onDelete: handleDelete }));
29
+ const handleImport = (data) => {
30
+ const newItems = data.map((item) => cleanItemValues({
31
+ ...item,
32
+ id: generateNodeID(schema.nodes.bibliography_item),
33
+ }));
34
+ props.onSave(newItems);
35
+ dispatch({
36
+ type: 'set',
37
+ state: [...items, ...newItems],
38
+ });
39
+ };
40
+ return (React.createElement(ReferencesModal, { isOpen: isOpen, onCancel: () => setOpen(false), items: items, item: selectedItem, citationCounts: props.citationCounts, onSave: handleSave, onDelete: handleDelete, handleImport: handleImport }));
25
41
  };
@@ -1,9 +1,11 @@
1
- import { Category, CitationCountIcon, CloseButton, Dialog, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, ScrollableModalContent, SidebarContent, StyledModal, useScrollDetection, withListNavigation, withNavigableListItem, } from '@manuscripts/style-guide';
1
+ import { AddAuthorIcon, Category, CitationCountIcon, CloseButton, Dialog, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, ScrollableModalContent, SidebarContent, StyledModal, useScrollDetection, withListNavigation, withNavigableListItem, } from '@manuscripts/style-guide';
2
2
  import isEqual from 'lodash/isEqual';
3
- import React, { useEffect, useRef, useState } from 'react';
3
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
4
4
  import styled from 'styled-components';
5
5
  import { ReferenceForm, } from './ReferenceForm/ReferenceForm';
6
6
  import { ReferenceLine } from './ReferenceLine';
7
+ import { ImportBibliographyModal } from './ImportBibliographyModal';
8
+ import { ImportSuccessPlaceholder } from './ImportSuccessPlaceholder';
7
9
  const ReferencesModalContainer = styled(ModalContainer) `
8
10
  min-width: 960px;
9
11
  `;
@@ -13,6 +15,33 @@ const ReferencesSidebar = styled(ModalSidebar) `
13
15
  const ReferencesSidebarContent = styled(SidebarContent) `
14
16
  overflow-y: auto;
15
17
  `;
18
+ const ImportFromFileFooter = styled.div `
19
+ padding: 16px;
20
+ `;
21
+ const ImportFromFileButton = styled.button `
22
+ display: flex;
23
+ width: 100%;
24
+ padding: 8px 24px;
25
+ justify-content: center;
26
+ align-items: center;
27
+ gap: 8px;
28
+ border-radius: 4px;
29
+ border: 1px dashed #c9c9c9;
30
+ background: #fafafa;
31
+ cursor: pointer;
32
+ font-family: ${(props) => props.theme.font.family.sans};
33
+ font-size: 14px;
34
+ color: #353535;
35
+
36
+ svg {
37
+ width: 20px;
38
+ height: 20px;
39
+ }
40
+
41
+ svg rect {
42
+ fill: #6e6e6e;
43
+ }
44
+ `;
16
45
  const ReferencesInnerWrapper = withListNavigation(styled.div `
17
46
  width: 100%;
18
47
  padding: 12px 0;
@@ -100,15 +129,25 @@ const normalize = (item) => ({
100
129
  accessed: item.accessed,
101
130
  comment: item.comment || '',
102
131
  });
103
- export const ReferencesModal = ({ isOpen, onCancel, items, item, citationCounts, onSave, onDelete, }) => {
132
+ export const ReferencesModal = ({ isOpen, onCancel, items, item, citationCounts, onSave, onDelete, handleImport, }) => {
133
+ const [importing, setImporting] = useState(false);
134
+ const [importSuccessCount, setImportSuccessCount] = useState(null);
104
135
  const [confirm, setConfirm] = useState(false);
105
136
  const valuesRef = useRef(undefined);
106
137
  const [selection, setSelection] = useState();
107
138
  const selectionRef = useRef(null);
139
+ const sortedItems = useMemo(() => [...items].sort((a, b) => {
140
+ const aUncited = (citationCounts.get(a.id) ?? 0) === 0;
141
+ const bUncited = (citationCounts.get(b.id) ?? 0) === 0;
142
+ if (aUncited === bUncited) {
143
+ return 0;
144
+ }
145
+ return aUncited ? 1 : -1;
146
+ }), [items, citationCounts]);
108
147
  const isSelected = (item) => {
109
148
  return item.id === selection?.id;
110
149
  };
111
- const selectionIndex = items.findIndex(isSelected);
150
+ const selectionIndex = sortedItems.findIndex(isSelected);
112
151
  useEffect(() => {
113
152
  setSelection(item);
114
153
  }, [item]);
@@ -126,8 +165,8 @@ export const ReferencesModal = ({ isOpen, onCancel, items, item, citationCounts,
126
165
  useEffect(() => {
127
166
  const base = Math.max(0, selectionIndex - selectionTopOffset);
128
167
  setStartIndex(base);
129
- setEndIndex(Math.min(items.length - 1, base + pageSize));
130
- }, [selectionIndex, items]);
168
+ setEndIndex(Math.min(sortedItems.length - 1, base + pageSize));
169
+ }, [selectionIndex, sortedItems]);
131
170
  useEffect(() => {
132
171
  if (triggers.top) {
133
172
  const newFirst = Math.max(0, startIndex - pageSize);
@@ -135,11 +174,11 @@ export const ReferencesModal = ({ isOpen, onCancel, items, item, citationCounts,
135
174
  setEndIndex(Math.min(newFirst + dropLimit, endIndex));
136
175
  }
137
176
  if (triggers.bottom) {
138
- const newLast = Math.min(items.length - 1, endIndex + pageSize);
177
+ const newLast = Math.min(sortedItems.length - 1, endIndex + pageSize);
139
178
  setEndIndex(newLast);
140
179
  setStartIndex(Math.max(newLast - dropLimit, startIndex));
141
180
  }
142
- }, [triggers, items]);
181
+ }, [triggers, sortedItems]);
143
182
  const actionsRef = useRef(undefined);
144
183
  const reset = () => {
145
184
  actionsRef.current?.reset();
@@ -173,7 +212,9 @@ export const ReferencesModal = ({ isOpen, onCancel, items, item, citationCounts,
173
212
  onDelete(selection);
174
213
  setSelection(undefined);
175
214
  };
215
+ const clearImportSuccess = () => setImportSuccessCount(null);
176
216
  const handleItemClick = (item) => {
217
+ clearImportSuccess();
177
218
  const values = valuesRef.current;
178
219
  if (values && selection && !isEqual(values, normalize(selection))) {
179
220
  setConfirm(true);
@@ -184,33 +225,45 @@ export const ReferencesModal = ({ isOpen, onCancel, items, item, citationCounts,
184
225
  const handleChange = (values) => {
185
226
  valuesRef.current = values;
186
227
  };
187
- if (items.length <= 0) {
228
+ const handleImportSave = (data) => {
229
+ handleImport(data);
230
+ setImporting(false);
231
+ setSelection(undefined);
232
+ setImportSuccessCount(data.length);
233
+ };
234
+ if (sortedItems.length <= 0) {
188
235
  return React.createElement(React.Fragment, null);
189
236
  }
190
- return (React.createElement(StyledModal, { isOpen: isOpen, onRequestClose: onCancel },
191
- React.createElement(Dialog, { isOpen: confirm, category: Category.confirmation, header: "You've made changes to this option", message: "Would you like to save or discard your changes?", actions: {
192
- secondary: {
193
- action: () => reset(),
194
- title: 'Discard',
195
- },
196
- primary: {
197
- action: () => handleSave(valuesRef.current),
198
- title: 'Save',
199
- },
200
- } }),
201
- React.createElement(ReferencesModalContainer, { "data-cy": 'references-editor' },
202
- React.createElement(ModalHeader, null,
203
- React.createElement(CloseButton, { onClick: onCancel })),
204
- React.createElement(ModalBody, null,
205
- React.createElement(ReferencesSidebar, null,
206
- React.createElement(ModalSidebarHeader, null,
207
- React.createElement(ModalSidebarTitle, null, "References")),
208
- React.createElement(ReferencesSidebarContent, { ref: ref },
209
- React.createElement(ReferencesInnerWrapper, null, items.slice(startIndex, endIndex + 1).map((item) => (React.createElement(ReferenceButton, { key: item.id, id: item.id, className: isSelected(item) ? 'selected' : '', onClick: () => handleItemClick(item), ref: isSelected(item) ? selectionRef : null },
210
- React.createElement(IconContainer, null,
211
- React.createElement(CitationCountIcon, null),
212
- (citationCounts.get(item.id) || 0) > 0 ? (React.createElement(CitationCount, { "data-tooltip-content": "Number of times used in the document" }, citationCounts.get(item.id))) : (React.createElement(CitationCount, { className: "unused" }, "0"))),
213
- React.createElement(ReferenceLine, { item: item }))))))),
214
- React.createElement(ScrollableModalContent, null, selection && (React.createElement(ReferenceForm, { values: normalize(selection), showDelete: !citationCounts.get(selection.id) &&
215
- isNewItem(selection, ['id', 'type']), onChange: handleChange, onCancel: onCancel, onDelete: handleDelete, onSave: handleSave, actionsRef: actionsRef })))))));
237
+ return (React.createElement(React.Fragment, null,
238
+ importing && (React.createElement(ImportBibliographyModal, { onCancel: () => setImporting(false), onSave: handleImportSave })),
239
+ React.createElement(StyledModal, { isOpen: isOpen, onRequestClose: onCancel },
240
+ React.createElement(Dialog, { isOpen: confirm, category: Category.confirmation, header: "You've made changes to this option", message: "Would you like to save or discard your changes?", actions: {
241
+ secondary: {
242
+ action: () => reset(),
243
+ title: 'Discard',
244
+ },
245
+ primary: {
246
+ action: () => handleSave(valuesRef.current),
247
+ title: 'Save',
248
+ },
249
+ } }),
250
+ React.createElement(ReferencesModalContainer, { "data-cy": 'references-editor' },
251
+ React.createElement(ModalHeader, null,
252
+ React.createElement(CloseButton, { "data-cy": "modal-close-button", onClick: onCancel })),
253
+ React.createElement(ModalBody, null,
254
+ React.createElement(ReferencesSidebar, null,
255
+ React.createElement(ModalSidebarHeader, null,
256
+ React.createElement(ModalSidebarTitle, null, "References")),
257
+ React.createElement(ReferencesSidebarContent, { ref: ref },
258
+ React.createElement(ReferencesInnerWrapper, null, sortedItems.slice(startIndex, endIndex + 1).map((item) => (React.createElement(ReferenceButton, { key: item.id, id: item.id, className: isSelected(item) ? 'selected' : '', onClick: () => handleItemClick(item), ref: isSelected(item) ? selectionRef : null },
259
+ React.createElement(IconContainer, null,
260
+ React.createElement(CitationCountIcon, null),
261
+ (citationCounts.get(item.id) || 0) > 0 ? (React.createElement(CitationCount, { "data-tooltip-content": "Number of times used in the document" }, citationCounts.get(item.id))) : (React.createElement(CitationCount, { className: "unused" }, "0"))),
262
+ React.createElement(ReferenceLine, { item: item })))))),
263
+ React.createElement(ImportFromFileFooter, null,
264
+ React.createElement(ImportFromFileButton, { type: "button", "data-cy": "import-from-file-button", onClick: () => setImporting(true) },
265
+ React.createElement(AddAuthorIcon, null),
266
+ "Import from file"))),
267
+ React.createElement(ScrollableModalContent, null, importSuccessCount !== null ? (React.createElement(ImportSuccessPlaceholder, { count: importSuccessCount })) : (selection && (React.createElement(ReferenceForm, { values: normalize(selection), showDelete: !citationCounts.get(selection.id) &&
268
+ isNewItem(selection, ['id', 'type']), onChange: handleChange, onCancel: onCancel, onDelete: handleDelete, onSave: handleSave, actionsRef: actionsRef })))))))));
216
269
  };
@@ -64,3 +64,39 @@ export const deleteNode = (view, id) => {
64
64
  view.dispatch(view.state.tr.delete(pos, pos + node.nodeSize));
65
65
  }
66
66
  };
67
+ const createBibliographySection = (nodes) => schema.nodes.bibliography_section.createAndFill({}, [
68
+ schema.nodes.section_title.create({}, schema.text('References')),
69
+ schema.nodes.bibliography_element.create({}, nodes),
70
+ ]);
71
+ export const insertBibliographyItems = (view, items) => {
72
+ const { doc } = view.state;
73
+ let tr = view.state.tr;
74
+ const biblioSection = utils.findChildrenByType(doc, schema.nodes.bibliography_element, true);
75
+ if (biblioSection.length) {
76
+ let insertPos = biblioSection[0].pos + 1;
77
+ for (const attrs of items) {
78
+ const node = schema.nodes.bibliography_item.create(attrs);
79
+ tr = tr.insert(insertPos, node);
80
+ insertPos += node.nodeSize;
81
+ }
82
+ }
83
+ else {
84
+ const backmatter = utils.findChildrenByType(doc, schema.nodes.backmatter, true);
85
+ const backmatterEnd = backmatter[0]
86
+ ? backmatter[0].node.nodeSize + backmatter[0].pos
87
+ : 0;
88
+ const nodes = items.map((attrs) => schema.nodes.bibliography_item.create(attrs));
89
+ tr = tr.insert(backmatterEnd ? backmatterEnd - 1 : tr.doc.content.size, createBibliographySection(nodes));
90
+ }
91
+ view.dispatch(tr);
92
+ };
93
+ export const saveBibliographyItem = (view, attrs) => {
94
+ if (attrs.length === 1) {
95
+ const item = attrs[0];
96
+ if (findChildByID(view, item.id)) {
97
+ updateNodeAttrs(view, schema.nodes.bibliography_item, item);
98
+ return;
99
+ }
100
+ }
101
+ insertBibliographyItems(view, attrs);
102
+ };
@@ -1,2 +1,2 @@
1
- export const VERSION = '3.16.1';
1
+ export const VERSION = '3.16.2';
2
2
  export const MATHJAX_VERSION = '3.2.2';
@@ -14,14 +14,13 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { ContextMenu } from '@manuscripts/style-guide';
17
- import { schema, } from '@manuscripts/transform';
18
17
  import { NodeSelection } from 'prosemirror-state';
19
18
  import { ReferencesEditor, } from '../components/references/ReferencesEditor';
20
19
  import { createCommentMarker, handleComment } from '../lib/comments';
21
20
  import { handleEnterKey } from '../lib/navigation-utils';
22
21
  import { findNodeByID } from '../lib/doc';
23
22
  import { sanitize } from '../lib/dompurify';
24
- import { deleteNode, findChildByID, updateNodeAttrs } from '../lib/view';
23
+ import { deleteNode, findChildByID, saveBibliographyItem } from '../lib/view';
25
24
  import { getBibliographyPluginState } from '../plugins/bibliography';
26
25
  import { commentsKey, setCommentSelection } from '../plugins/comments';
27
26
  import { selectedSuggestionKey } from '../plugins/selected-suggestion';
@@ -86,7 +85,7 @@ export class BibliographyElementBlockView extends BlockView {
86
85
  this.dom.appendChild(this.container);
87
86
  };
88
87
  this.handleSave = (attrs) => {
89
- updateNodeAttrs(this.view, schema.nodes.bibliography_item, attrs);
88
+ saveBibliographyItem(this.view, attrs);
90
89
  };
91
90
  this.handleDelete = (item) => {
92
91
  deleteNode(this.view, item.id);
@@ -15,23 +15,17 @@
15
15
  */
16
16
  import { ContextMenu } from '@manuscripts/style-guide';
17
17
  import { isDeleted } from '@manuscripts/track-changes-plugin';
18
- import { schema, } from '@manuscripts/transform';
19
18
  import { TextSelection } from 'prosemirror-state';
20
- import { findChildrenByType } from 'prosemirror-utils';
21
19
  import { CitationEditor, } from '../components/references/CitationEditor';
22
20
  import { CitationViewer, } from '../components/references/CitationViewer';
23
21
  import { handleComment } from '../lib/comments';
24
22
  import { Crossref } from '../lib/crossref';
25
23
  import { handleEnterKey } from '../lib/navigation-utils';
26
- import { deleteNode, findChildByID, updateNodeAttrs } from '../lib/view';
24
+ import { deleteNode, saveBibliographyItem } from '../lib/view';
27
25
  import { getBibliographyPluginState } from '../plugins/bibliography';
28
26
  import { CitationView } from './citation';
29
27
  import { createEditableNodeView } from './creators';
30
28
  import ReactSubView from './ReactSubView';
31
- const createBibliographySection = (node) => schema.nodes.bibliography_section.createAndFill({}, [
32
- schema.nodes.section_title.create({}, schema.text('References')),
33
- schema.nodes.bibliography_element.create({}, node ? [node] : []),
34
- ]);
35
29
  export class CitationEditableView extends CitationView {
36
30
  constructor() {
37
31
  super(...arguments);
@@ -137,12 +131,7 @@ export class CitationEditableView extends CitationView {
137
131
  this.props.popper.destroy();
138
132
  };
139
133
  this.handleSave = (attrs) => {
140
- if (!findChildByID(this.view, attrs.id)) {
141
- this.insertBibliographyNode(attrs);
142
- }
143
- else {
144
- updateNodeAttrs(this.view, schema.nodes.bibliography_item, attrs);
145
- }
134
+ saveBibliographyItem(this.view, attrs);
146
135
  };
147
136
  this.handleUncite = (id) => {
148
137
  const attrs = this.node.attrs;
@@ -175,7 +164,7 @@ export class CitationEditableView extends CitationView {
175
164
  item.id = existingItem.id;
176
165
  }
177
166
  else {
178
- this.insertBibliographyNode(item);
167
+ saveBibliographyItem(this.view, [item]);
179
168
  }
180
169
  rids.push(item.id);
181
170
  }
@@ -194,21 +183,5 @@ export class CitationEditableView extends CitationView {
194
183
  this.dom.addEventListener('mouseup', () => this.handleClick(false));
195
184
  this.dom.addEventListener('keydown', handleEnterKey(() => this.handleClick(true)));
196
185
  }
197
- insertBibliographyNode(attrs) {
198
- const { doc, tr } = this.view.state;
199
- const biblioSection = findChildrenByType(doc, schema.nodes.bibliography_element, true);
200
- const backmatter = findChildrenByType(doc, schema.nodes.backmatter, true);
201
- const backmatterEnd = backmatter[0]
202
- ? backmatter[0].node.nodeSize + backmatter[0].pos
203
- : 0;
204
- const node = schema.nodes.bibliography_item.create(attrs);
205
- if (biblioSection.length) {
206
- this.view.dispatch(tr.insert(biblioSection[0].pos + 1, node));
207
- }
208
- else {
209
- this.view.dispatch(tr.insert(backmatterEnd ? backmatterEnd - 1 : tr.doc.content.size, createBibliographySection(node)));
210
- }
211
- return false;
212
- }
213
186
  }
214
187
  export default createEditableNodeView(CitationEditableView);
@@ -9,7 +9,7 @@ export interface CitationEditorProps {
9
9
  sources: BibliographyItemSource[];
10
10
  onCite: (items: BibliographyItemAttrs[]) => void;
11
11
  onUncite: (id: string) => void;
12
- onSave: (item: BibliographyItemAttrs) => void;
12
+ onSave: (item: BibliographyItemAttrs[]) => void;
13
13
  onDelete: (item: BibliographyItemAttrs) => void;
14
14
  onCancel: () => void;
15
15
  canEdit: boolean;
@@ -0,0 +1,7 @@
1
+ import { BibliographyItemAttrs } from '@manuscripts/transform';
2
+ import React from 'react';
3
+ export interface ImportBibliographyProps {
4
+ onCancel: () => void;
5
+ onContinue: (data: BibliographyItemAttrs[]) => void;
6
+ }
7
+ export declare const ImportBibliography: React.FC<ImportBibliographyProps>;
@@ -0,0 +1,8 @@
1
+ import { BibliographyItemAttrs } from '@manuscripts/transform';
2
+ import React from 'react';
3
+ export interface ImportReferencesConfirmationProps {
4
+ items: BibliographyItemAttrs[];
5
+ onCancel: () => void;
6
+ onConfirm: (items: BibliographyItemAttrs[]) => void;
7
+ }
8
+ export declare const ImportReferencesConfirmation: React.FC<ImportReferencesConfirmationProps>;
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ export interface ImportSuccessPlaceholderProps {
3
+ count: number;
4
+ }
5
+ export declare const ImportSuccessPlaceholder: React.FC<ImportSuccessPlaceholderProps>;
@@ -21,7 +21,6 @@ export declare const ReferenceSearch: React.FC<{
21
21
  sources: BibliographyItemSource[];
22
22
  items: BibliographyItemAttrs[];
23
23
  onAdd: () => void;
24
- onImport: () => void;
25
24
  onCite: (items: BibliographyItemAttrs[]) => void;
26
25
  onCancel: () => void;
27
26
  }>;
@@ -1,4 +1,7 @@
1
+ import { BibliographyItemAttrs } from '@manuscripts/transform';
1
2
  import React from 'react';
2
3
  import { ReferencesModalProps } from './ReferencesModal';
3
- export type ReferencesEditorProps = Omit<ReferencesModalProps, 'isOpen' | 'onCancel'>;
4
+ export type ReferencesEditorProps = Omit<ReferencesModalProps, 'isOpen' | 'onCancel' | 'handleImport' | 'onSave'> & {
5
+ onSave: (item: BibliographyItemAttrs[]) => void;
6
+ };
4
7
  export declare const ReferencesEditor: React.FC<ReferencesEditorProps>;
@@ -8,5 +8,6 @@ export interface ReferencesModalProps {
8
8
  citationCounts: Map<string, number>;
9
9
  onSave: (item: BibliographyItemAttrs) => void;
10
10
  onDelete: (item: BibliographyItemAttrs) => void;
11
+ handleImport: (data: BibliographyItemAttrs[]) => void;
11
12
  }
12
13
  export declare const ReferencesModal: React.FC<ReferencesModalProps>;
@@ -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 { ManuscriptEditorView, ManuscriptNodeType } from '@manuscripts/transform';
16
+ import { BibliographyItemAttrs, ManuscriptEditorView, ManuscriptNodeType } from '@manuscripts/transform';
17
17
  import { Attrs } from 'prosemirror-model';
18
18
  import * as utils from 'prosemirror-utils';
19
19
  export declare const findChildByID: (view: ManuscriptEditorView, id: string) => utils.NodeWithPos | undefined;
@@ -22,3 +22,5 @@ export declare const findChildrenByType: (view: ManuscriptEditorView, type: Manu
22
22
  export declare const findChildrenAttrsByType: <T extends Attrs>(view: ManuscriptEditorView, type: ManuscriptNodeType) => T[];
23
23
  export declare const updateNodeAttrs: (view: ManuscriptEditorView, type: ManuscriptNodeType, attrs: Attrs) => boolean;
24
24
  export declare const deleteNode: (view: ManuscriptEditorView, id: string) => void;
25
+ export declare const insertBibliographyItems: (view: ManuscriptEditorView, items: BibliographyItemAttrs[]) => void;
26
+ export declare const saveBibliographyItem: (view: ManuscriptEditorView, attrs: BibliographyItemAttrs[]) => void;
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "3.16.1";
1
+ export declare const VERSION = "3.16.2";
2
2
  export declare const MATHJAX_VERSION = "3.2.2";
@@ -31,7 +31,6 @@ export declare class CitationEditableView extends CitationView {
31
31
  private handleUncite;
32
32
  private handleCite;
33
33
  private handleDelete;
34
- private insertBibliographyNode;
35
34
  }
36
35
  declare const _default: (props: import("../configs/ManuscriptsEditor").EditorProps, dispatch?: import("..").Dispatch) => import("../types").NodeViewCreator<CitationEditableView>;
37
36
  export default _default;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@manuscripts/body-editor",
3
3
  "description": "Prosemirror components for editing and viewing manuscripts",
4
- "version": "3.16.1",
4
+ "version": "3.16.2",
5
5
  "repository": "github:Atypon-OpenSource/manuscripts-body-editor",
6
6
  "license": "Apache-2.0",
7
7
  "main": "dist/cjs",