@manuscripts/body-editor 3.16.0 → 3.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/cjs/commands.js +45 -10
  2. package/dist/cjs/components/form/FormFooter.js +2 -2
  3. package/dist/cjs/components/keyboard-shortcuts-modal/KeyboardShortcutsModal.js +4 -2
  4. package/dist/cjs/components/views/CrossReferenceItems.js +139 -52
  5. package/dist/cjs/components/views/DeleteSupplementDialog.js +1 -1
  6. package/dist/cjs/lib/supplements.js +9 -1
  7. package/dist/cjs/menus.js +35 -0
  8. package/dist/cjs/plugins/objects.js +1 -1
  9. package/dist/cjs/toolbar.js +6 -0
  10. package/dist/cjs/versions.js +1 -1
  11. package/dist/cjs/views/cross_reference.js +12 -2
  12. package/dist/cjs/views/cross_reference_editable.js +95 -4
  13. package/dist/cjs/views/figure_editable.js +3 -0
  14. package/dist/es/commands.js +43 -9
  15. package/dist/es/components/form/FormFooter.js +2 -2
  16. package/dist/es/components/keyboard-shortcuts-modal/KeyboardShortcutsModal.js +4 -2
  17. package/dist/es/components/views/CrossReferenceItems.js +141 -54
  18. package/dist/es/components/views/DeleteSupplementDialog.js +2 -2
  19. package/dist/es/lib/supplements.js +8 -1
  20. package/dist/es/menus.js +36 -1
  21. package/dist/es/plugins/objects.js +1 -1
  22. package/dist/es/toolbar.js +8 -2
  23. package/dist/es/versions.js +1 -1
  24. package/dist/es/views/cross_reference.js +12 -2
  25. package/dist/es/views/cross_reference_editable.js +96 -5
  26. package/dist/es/views/figure_editable.js +3 -0
  27. package/dist/types/commands.d.ts +1 -0
  28. package/dist/types/components/form/FormFooter.d.ts +2 -1
  29. package/dist/types/components/views/CrossReferenceItems.d.ts +3 -0
  30. package/dist/types/lib/supplements.d.ts +1 -0
  31. package/dist/types/versions.d.ts +1 -1
  32. package/dist/types/views/cross_reference_editable.d.ts +6 -0
  33. package/package.json +4 -4
  34. package/src/commands.ts +52 -10
  35. package/src/components/ChangeHandlingForm.tsx +4 -2
  36. package/src/components/form/FormFooter.tsx +3 -1
  37. package/src/components/keyboard-shortcuts-modal/KeyboardShortcutsModal.tsx +6 -7
  38. package/src/components/toolbar/InsertEmbedDialog.tsx +6 -2
  39. package/src/components/toolbar/type-selector/styles.ts +2 -1
  40. package/src/components/views/CrossReferenceItems.tsx +227 -100
  41. package/src/components/views/DeleteSupplementDialog.tsx +3 -6
  42. package/src/lib/footnotes.ts +3 -1
  43. package/src/lib/supplements.ts +13 -1
  44. package/src/lib/utils.ts +15 -4
  45. package/src/menus.tsx +39 -0
  46. package/src/plugins/add-subtitle.ts +18 -16
  47. package/src/plugins/objects.ts +2 -6
  48. package/src/toolbar.tsx +9 -0
  49. package/src/versions.ts +1 -1
  50. package/src/views/cross_reference.ts +17 -3
  51. package/src/views/cross_reference_editable.ts +123 -11
  52. package/src/views/figure_editable.ts +4 -0
  53. package/src/views/supplement.ts +0 -1
  54. package/src/views/supplements.ts +2 -2
@@ -13,10 +13,14 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { skipTracking } from '@manuscripts/track-changes-plugin';
16
+ import { ContextMenu } from '@manuscripts/style-guide';
17
+ import { isDeleted, skipTracking } from '@manuscripts/track-changes-plugin';
17
18
  import { schema } from '@manuscripts/transform';
18
19
  import { TextSelection } from 'prosemirror-state';
19
20
  import { CrossReferenceItems } from '../components/views/CrossReferenceItems';
21
+ import { handleComment } from '../lib/comments';
22
+ import { findNodeByID } from '../lib/doc';
23
+ import { getSupplementDisplayLabel } from '../lib/supplements';
20
24
  import { objectsKey } from '../plugins/objects';
21
25
  import { createEditableNodeView } from './creators';
22
26
  import { CrossReferenceView } from './cross_reference';
@@ -31,14 +35,20 @@ export class CrossReferenceEditableView extends CrossReferenceView {
31
35
  if (!can?.editArticle || rids.length) {
32
36
  return;
33
37
  }
38
+ this.showPicker();
39
+ };
40
+ this.showPicker = () => {
41
+ const rids = this.node.attrs.rids;
34
42
  const componentProps = {
35
43
  handleSelect: this.handleSelect,
36
44
  targets: this.getTargets(),
45
+ files: this.props.getFiles(),
37
46
  handleCancel: this.handleCancel,
38
47
  currentTargetId: rids[0],
39
48
  currentCustomLabel: this.node.attrs.label,
49
+ isEdit: rids.length > 0,
40
50
  };
41
- this.popperContainer = ReactSubView(this.props, CrossReferenceItems, componentProps, this.node, this.getPos, this.view, ['cross-reference-editor']);
51
+ this.popperContainer = ReactSubView(this.props, CrossReferenceItems, componentProps, this.node, this.getPos, this.view);
42
52
  this.popperContainer.setAttribute('tabindex', '0');
43
53
  this.props.popper.show(this.dom, this.popperContainer, 'auto');
44
54
  };
@@ -50,15 +60,39 @@ export class CrossReferenceEditableView extends CrossReferenceView {
50
60
  this.handleCancel();
51
61
  };
52
62
  this.getTargets = () => {
53
- const excludedTypes = [schema.nodes.image_element.name];
54
63
  const targets = objectsKey.getState(this.view.state);
55
- return Array.from(targets.values()).filter((t) => !excludedTypes.includes(t.type));
64
+ const files = this.props.getFiles();
65
+ const fileMap = new Map(files.map((f) => [f.id, f.name]));
66
+ const excludedTypes = [schema.nodes.image_element.name];
67
+ const supplement = schema.nodes.supplement.name;
68
+ return Array.from(targets.values()).reduce((acc, t) => {
69
+ if (excludedTypes.includes(t.type)) {
70
+ return acc;
71
+ }
72
+ if (t.type === supplement && t.href) {
73
+ const found = findNodeByID(this.view.state.doc, t.id);
74
+ const label = found
75
+ ? getSupplementDisplayLabel(found.node, files)
76
+ : (fileMap.get(t.href) ?? t.href);
77
+ acc.push({ ...t, label, caption: '' });
78
+ }
79
+ else {
80
+ acc.push(t);
81
+ }
82
+ return acc;
83
+ }, []);
56
84
  };
57
85
  this.handleCancel = () => {
58
86
  if (!this.node.attrs.rids.length) {
59
87
  const { state } = this.view;
60
88
  const pos = this.getPos();
61
- const tr = state.tr.delete(pos, pos + this.node.nodeSize);
89
+ if (pos === undefined) {
90
+ return;
91
+ }
92
+ const label = this.node.attrs.label;
93
+ const tr = label
94
+ ? state.tr.replaceWith(pos, pos + this.node.nodeSize, state.schema.text(label))
95
+ : state.tr.delete(pos, pos + this.node.nodeSize);
62
96
  tr.setSelection(TextSelection.create(tr.doc, pos));
63
97
  skipTracking(tr);
64
98
  this.view.dispatch(tr);
@@ -79,6 +113,63 @@ export class CrossReferenceEditableView extends CrossReferenceView {
79
113
  this.view.dispatch(tr.setSelection(selection));
80
114
  this.destroy();
81
115
  };
116
+ this.handleClick = () => {
117
+ if (isDeleted(this.node)) {
118
+ return;
119
+ }
120
+ if (!this.node.attrs.rids.length) {
121
+ return;
122
+ }
123
+ this.showContextMenu();
124
+ };
125
+ this.showContextMenu = () => {
126
+ this.props.popper.destroy();
127
+ const can = this.props.getCapabilities();
128
+ const targets = objectsKey.getState(this.view.state);
129
+ const rid = this.node.attrs.rids[0];
130
+ const isOrphaned = !targets?.get(rid);
131
+ const actions = [
132
+ {
133
+ label: 'Comment',
134
+ icon: 'AddComment',
135
+ action: () => {
136
+ this.props.popper.destroy();
137
+ handleComment(this.node, this.view);
138
+ },
139
+ },
140
+ {
141
+ label: 'Go to content',
142
+ icon: 'Scroll',
143
+ action: () => this.navigateToTarget(),
144
+ disabled: isOrphaned,
145
+ },
146
+ ];
147
+ if (can?.editArticle) {
148
+ actions.unshift({
149
+ label: 'Edit',
150
+ icon: 'Edit',
151
+ action: () => this.handleEdit(),
152
+ disabled: isOrphaned,
153
+ });
154
+ }
155
+ this.contextMenu = ReactSubView(this.props, ContextMenu, { actions }, this.node, this.getPos, this.view, ['context-menu']);
156
+ this.props.popper.show(this.dom, this.contextMenu, 'right-start', false);
157
+ };
158
+ this.navigateToTarget = () => {
159
+ this.props.popper.destroy();
160
+ const rids = this.node.attrs.rids;
161
+ if (!rids.length) {
162
+ return;
163
+ }
164
+ this.props.navigate({
165
+ pathname: this.props.location.pathname,
166
+ hash: '#' + rids[0],
167
+ });
168
+ };
169
+ this.handleEdit = () => {
170
+ this.props.popper.destroy();
171
+ this.showPicker();
172
+ };
82
173
  }
83
174
  }
84
175
  export default createEditableNodeView(CrossReferenceEditableView);
@@ -195,6 +195,9 @@ export class FigureEditableView extends FigureView {
195
195
  this.addTools();
196
196
  }
197
197
  addTools() {
198
+ if (this.getPos() === undefined) {
199
+ return;
200
+ }
198
201
  this.manageReactTools();
199
202
  const existingDragHandlers = this.container.querySelectorAll('.drag-handler');
200
203
  existingDragHandlers.forEach((handler) => handler.remove());
@@ -44,6 +44,7 @@ export declare const findPosBeforeFirstSubsection: ($pos: ManuscriptResolvedPos)
44
44
  export declare const insertSectionLabel: (state: ManuscriptEditorState, dispatch?: Dispatch, currentTr?: Transaction) => boolean;
45
45
  export declare const insertLink: (state: ManuscriptEditorState, dispatch?: Dispatch) => boolean;
46
46
  export declare const insertInlineCitation: (state: ManuscriptEditorState, dispatch?: Dispatch) => boolean;
47
+ export declare const canInsertCrossReference: (state: ManuscriptEditorState) => boolean;
47
48
  export declare const insertCrossReference: (state: ManuscriptEditorState, dispatch?: Dispatch) => boolean;
48
49
  export declare const insertInlineEquation: (state: ManuscriptEditorState, dispatch?: Dispatch) => boolean;
49
50
  export declare const insertTableElementFooter: (tr: Transaction, table: [ManuscriptNode, number]) => {
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
- declare const FormFooter: ({ onCancel, primaryAction, }: {
2
+ declare const FormFooter: ({ onCancel, primaryAction, cancelLabel, }: {
3
3
  onCancel: () => void;
4
4
  primaryAction?: React.ReactNode;
5
+ cancelLabel?: string;
5
6
  }) => React.JSX.Element;
6
7
  export default FormFooter;
@@ -15,12 +15,15 @@
15
15
  */
16
16
  import { Target } from '@manuscripts/transform';
17
17
  import React from 'react';
18
+ import { FileAttachment } from '../../lib/files';
18
19
  interface Props {
19
20
  targets: Target[];
21
+ files: FileAttachment[];
20
22
  handleSelect: (id: string, customLabel?: string) => void;
21
23
  handleCancel: () => void;
22
24
  currentTargetId?: string;
23
25
  currentCustomLabel?: string;
26
+ isEdit?: boolean;
24
27
  }
25
28
  export declare const CrossReferenceItems: React.FC<Props>;
26
29
  export {};
@@ -18,6 +18,7 @@ export type NodeWeblink = {
18
18
  node: SupplementNode;
19
19
  pos: number;
20
20
  };
21
+ export declare const getSupplementCaptionTitle: (node: SupplementNode) => string;
21
22
  export declare const getSupplementDisplayLabel: (node: SupplementNode, files: {
22
23
  id: string;
23
24
  name: string;
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "3.16.0";
1
+ export declare const VERSION = "3.16.1";
2
2
  export declare const MATHJAX_VERSION = "3.2.2";
@@ -17,12 +17,18 @@ import { Target } from '@manuscripts/transform';
17
17
  import { CrossReferenceView } from './cross_reference';
18
18
  export declare class CrossReferenceEditableView extends CrossReferenceView {
19
19
  protected popperContainer: HTMLDivElement;
20
+ protected contextMenu: HTMLElement;
20
21
  selectNode: () => void;
22
+ showPicker: () => void;
21
23
  destroy: () => void;
22
24
  deselectNode: () => void;
23
25
  getTargets: () => Target[];
24
26
  handleCancel: () => void;
25
27
  handleSelect: (rid: string, label?: string) => Promise<void>;
28
+ handleClick: () => void;
29
+ showContextMenu: () => void;
30
+ private navigateToTarget;
31
+ private handleEdit;
26
32
  }
27
33
  declare const _default: (props: import("../configs/ManuscriptsEditor").EditorProps, dispatch?: import("..").Dispatch) => import("../types").NodeViewCreator<CrossReferenceEditableView>;
28
34
  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.0",
4
+ "version": "3.16.1",
5
5
  "repository": "github:Atypon-OpenSource/manuscripts-body-editor",
6
6
  "license": "Apache-2.0",
7
7
  "main": "dist/cjs",
@@ -41,9 +41,9 @@
41
41
  "@citation-js/plugin-pubmed": "0.3.0",
42
42
  "@citation-js/plugin-ris": "0.7.18",
43
43
  "@iarna/word-count": "1.1.2",
44
- "@manuscripts/style-guide": "3.7.0",
44
+ "@manuscripts/style-guide": "3.7.1",
45
45
  "@manuscripts/track-changes-plugin": "2.4.0",
46
- "@manuscripts/transform": "4.5.0",
46
+ "@manuscripts/transform": "4.5.1",
47
47
  "@popperjs/core": "2.11.8",
48
48
  "citeproc": "2.4.63",
49
49
  "codemirror": "5.65.19",
@@ -118,4 +118,4 @@
118
118
  "engines": {
119
119
  "node": ">=22"
120
120
  }
121
- }
121
+ }
package/src/commands.ts CHANGED
@@ -14,7 +14,11 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { isDeleted, skipTracking, skipSelect } from '@manuscripts/track-changes-plugin'
17
+ import {
18
+ isDeleted,
19
+ skipTracking,
20
+ skipSelect,
21
+ } from '@manuscripts/track-changes-plugin'
18
22
  import {
19
23
  AttachmentNode,
20
24
  AwardNode,
@@ -525,7 +529,7 @@ export const insertSupplementWeblink = (
525
529
  const supplement = schema.nodes.supplement.createAndFill(
526
530
  {
527
531
  id: generateNodeID(schema.nodes.supplement),
528
- href: url
532
+ href: url,
529
533
  },
530
534
  createAndFillCaption()
531
535
  ) as SupplementNode
@@ -554,7 +558,7 @@ export const updateSupplementWeblink = (
554
558
  ...node.attrs,
555
559
  href: url,
556
560
  title: title,
557
- })
561
+ })
558
562
  view.dispatch(tr)
559
563
  return true
560
564
  }
@@ -782,21 +786,62 @@ export const insertInlineCitation = (
782
786
  return true
783
787
  }
784
788
 
789
+ export const canInsertCrossReference = (
790
+ state: ManuscriptEditorState
791
+ ): boolean => {
792
+ if (!canInsert(schema.nodes.cross_reference)(state)) {
793
+ return false
794
+ }
795
+ const { from, to, empty, $from, $to } = state.selection
796
+ if (empty) {
797
+ return true
798
+ }
799
+ if (!$from.sameParent($to)) {
800
+ return false
801
+ }
802
+ let overlaps = false
803
+ state.doc.nodesBetween(from, to, (node) => {
804
+ if (node.isAtom && !node.isText) {
805
+ overlaps = true
806
+ return false
807
+ }
808
+ return !overlaps
809
+ })
810
+ return !overlaps
811
+ }
812
+
785
813
  export const insertCrossReference = (
786
814
  state: ManuscriptEditorState,
787
815
  dispatch?: Dispatch
788
816
  ) => {
817
+ if (!canInsertCrossReference(state)) {
818
+ return false
819
+ }
820
+
821
+ const text = selectedText()
822
+
789
823
  const node = state.schema.nodes.cross_reference.create({
824
+ id: generateNodeID(schema.nodes.cross_reference),
790
825
  rids: [],
826
+ label: text,
791
827
  })
792
828
 
793
- const pos = state.selection.to
829
+ const { tr } = state
830
+ let pos: number
831
+ const isWrap = !state.selection.empty
794
832
 
795
- const tr = state.tr.insert(pos, node)
833
+ if (isWrap) {
834
+ pos = state.selection.from
835
+ tr.replaceSelectionWith(node)
836
+ } else {
837
+ pos = state.selection.to
838
+ tr.insert(pos, node)
839
+ }
796
840
 
797
841
  if (dispatch) {
798
842
  const selection = NodeSelection.create(tr.doc, pos)
799
- dispatch(tr.setSelection(selection).scrollIntoView())
843
+ tr.setSelection(selection).scrollIntoView()
844
+ dispatch(isWrap ? skipTracking(tr) : tr)
800
845
  }
801
846
 
802
847
  return true
@@ -1524,10 +1569,7 @@ export const insertTransGraphicalAbstract =
1524
1569
  lang,
1525
1570
  category: category.id,
1526
1571
  },
1527
- [
1528
- schema.nodes.section_title.create(),
1529
- createAndFillFigureElement(state),
1530
- ]
1572
+ [schema.nodes.section_title.create(), createAndFillFigureElement(state)]
1531
1573
  ) as TransGraphicalAbstractNode
1532
1574
 
1533
1575
  const tr = state.tr.insert(pos, node)
@@ -17,8 +17,10 @@ import { Form, useFormikContext } from 'formik'
17
17
  import React, { MutableRefObject, PropsWithChildren, useEffect } from 'react'
18
18
  import styled from 'styled-components'
19
19
 
20
- export interface ChangeHandlingFormProps<Values>
21
- extends Omit<React.FormHTMLAttributes<HTMLFormElement>, 'onChange'> {
20
+ export interface ChangeHandlingFormProps<Values> extends Omit<
21
+ React.FormHTMLAttributes<HTMLFormElement>,
22
+ 'onChange'
23
+ > {
22
24
  onChange: (values: Values) => void
23
25
  id?: string
24
26
  formRef?: MutableRefObject<HTMLFormElement | null>
@@ -37,14 +37,16 @@ const StyledIconTextButton = styled(IconTextButton)`
37
37
  const FormFooter = ({
38
38
  onCancel,
39
39
  primaryAction,
40
+ cancelLabel = 'Close',
40
41
  }: {
41
42
  onCancel: () => void
42
43
  primaryAction?: React.ReactNode
44
+ cancelLabel?: string
43
45
  }) => {
44
46
  return (
45
47
  <Footer>
46
48
  <StyledIconTextButton color="secondary" onClick={onCancel}>
47
- Close
49
+ {cancelLabel}
48
50
  </StyledIconTextButton>
49
51
  {primaryAction}
50
52
  </Footer>
@@ -86,14 +86,11 @@ export const KeyboardShortcutsModal: React.FC<KeyboardShortcutsModalProps> = ({
86
86
  </StyledModalSidebarTitle>
87
87
  </StyledModalSidebarHeader>
88
88
  <StyledSidebarContent>
89
- <ShortcutTabs
90
- selectedIndex={tabIndex}
91
- onChange={setTabIndex}
92
- >
89
+ <ShortcutTabs selectedIndex={tabIndex} onChange={setTabIndex}>
93
90
  <ModalTabsWrapper>
94
91
  <ModalTabs tabLabels={TAB_LABELS} />
95
92
  </ModalTabsWrapper>
96
-
93
+
97
94
  <InspectorTabPanels>
98
95
  {EDITOR_KEYBOARD_SHORTCUT_TABS.map((tab) => (
99
96
  <ShortcutTabPanel key={tab.id}>
@@ -139,7 +136,8 @@ const ShortcutTabs = styled(InspectorTabs)`
139
136
  `
140
137
 
141
138
  const ModalTabsWrapper = styled('div')`
142
- margin: 0 ${(props) => props.theme.grid.unit * 8}px ${(props) => props.theme.grid.unit * 3}px;
139
+ margin: 0 ${(props) => props.theme.grid.unit * 8}px
140
+ ${(props) => props.theme.grid.unit * 3}px;
143
141
  `
144
142
 
145
143
  const ShortcutTabPanel = styled(InspectorTabPanel).attrs({
@@ -198,7 +196,8 @@ const Section = styled.section`
198
196
  `
199
197
 
200
198
  const SectionTitle = styled.h3`
201
- margin: 0 ${(props) => props.theme.grid.unit * 8}px ${(props) => props.theme.grid.unit * 2}px;
199
+ margin: 0 ${(props) => props.theme.grid.unit * 8}px
200
+ ${(props) => props.theme.grid.unit * 2}px;
202
201
  font-size: ${(props) => props.theme.font.size.large};
203
202
  font-weight: ${(props) => props.theme.font.weight.normal};
204
203
  line-height: ${(props) => props.theme.font.lineHeight.large};
@@ -93,8 +93,12 @@ export const InsertEmbedDialog: React.FC<InsertEmbedDialogProps> = ({
93
93
  () => {
94
94
  debounce(async () => {
95
95
  if (url && allowedHref(url)) {
96
- const html = await getEditorProps(state).fetchOEmbedHtml(url, 368, 217)
97
- setOEmbedHTML(html)
96
+ const html = await getEditorProps(state).fetchOEmbedHtml(
97
+ url,
98
+ 368,
99
+ 217
100
+ )
101
+ setOEmbedHTML(html)
98
102
  }
99
103
  })
100
104
  },
@@ -18,7 +18,8 @@ import styled from 'styled-components'
18
18
 
19
19
  import { Option } from './TypeSelector'
20
20
 
21
- const TypeSelect: React.ComponentType<Props<Option, false, GroupBase<Option>>> = Select
21
+ const TypeSelect: React.ComponentType<Props<Option, false, GroupBase<Option>>> =
22
+ Select
22
23
 
23
24
  export const StyledSelect = styled(TypeSelect)`
24
25
  .type-selector__control:hover {