@manuscripts/body-editor 3.19.0 → 3.19.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 (53) hide show
  1. package/dist/cjs/components/cross-ref-check-modal/CrossRefWarningModal.js +202 -52
  2. package/dist/cjs/components/cross-ref-check-modal/openModal.js +8 -2
  3. package/dist/cjs/components/references/ReferenceForm/ReferenceForm.js +22 -40
  4. package/dist/cjs/components/references/ReferenceForm/styled-components.js +1 -13
  5. package/dist/cjs/components/references/ReferencesEditor.js +26 -1
  6. package/dist/cjs/components/references/ReferencesModal.js +71 -7
  7. package/dist/cjs/lib/utils.js +12 -1
  8. package/dist/cjs/menus.js +12 -0
  9. package/dist/cjs/plugins/cross-references.js +37 -14
  10. package/dist/cjs/versions.js +1 -1
  11. package/dist/cjs/views/ReactSubView.js +1 -0
  12. package/dist/cjs/views/cross_reference.js +13 -2
  13. package/dist/cjs/views/cross_reference_editable.js +1 -4
  14. package/dist/es/components/cross-ref-check-modal/CrossRefWarningModal.js +202 -52
  15. package/dist/es/components/cross-ref-check-modal/openModal.js +9 -3
  16. package/dist/es/components/references/ReferenceForm/ReferenceForm.js +21 -40
  17. package/dist/es/components/references/ReferenceForm/styled-components.js +0 -12
  18. package/dist/es/components/references/ReferencesEditor.js +21 -0
  19. package/dist/es/components/references/ReferencesModal.js +73 -9
  20. package/dist/es/lib/utils.js +11 -1
  21. package/dist/es/menus.js +12 -0
  22. package/dist/es/plugins/cross-references.js +37 -15
  23. package/dist/es/versions.js +1 -1
  24. package/dist/es/views/ReactSubView.js +1 -0
  25. package/dist/es/views/cross_reference.js +13 -2
  26. package/dist/es/views/cross_reference_editable.js +1 -4
  27. package/dist/types/components/authors-affiliations/AuthorsAndAffiliationsModals.d.ts +1 -1
  28. package/dist/types/components/cross-ref-check-modal/CrossRefWarningModal.d.ts +4 -2
  29. package/dist/types/components/cross-ref-check-modal/openModal.d.ts +2 -2
  30. package/dist/types/components/references/ReferenceForm/ReferenceForm.d.ts +3 -3
  31. package/dist/types/components/references/ReferenceForm/styled-components.d.ts +0 -1
  32. package/dist/types/components/references/ReferencesEditor.d.ts +3 -0
  33. package/dist/types/lib/utils.d.ts +4 -0
  34. package/dist/types/plugins/cross-references.d.ts +2 -0
  35. package/dist/types/versions.d.ts +1 -1
  36. package/dist/types/views/ReactSubView.d.ts +5 -2
  37. package/dist/types/views/cross_reference.d.ts +1 -0
  38. package/package.json +2 -2
  39. package/src/components/cross-ref-check-modal/CrossRefWarningModal.tsx +315 -83
  40. package/src/components/cross-ref-check-modal/openModal.ts +14 -4
  41. package/src/components/references/ReferenceForm/ReferenceForm.tsx +21 -82
  42. package/src/components/references/ReferenceForm/styled-components.ts +0 -13
  43. package/src/components/references/ReferencesEditor.tsx +38 -0
  44. package/src/components/references/ReferencesModal.tsx +116 -14
  45. package/src/lib/utils.ts +24 -0
  46. package/src/lib/view.ts +1 -0
  47. package/src/menus.tsx +12 -0
  48. package/src/plugins/cross-references.ts +41 -13
  49. package/src/versions.ts +1 -1
  50. package/src/views/ReactSubView.tsx +6 -3
  51. package/src/views/cross_reference.ts +14 -2
  52. package/src/views/cross_reference_editable.ts +1 -4
  53. package/styles/Editor.css +12 -0
@@ -37,19 +37,6 @@ export const Button = styled(IconButton).attrs({
37
37
  }
38
38
  `
39
39
 
40
- export const Actions = styled.div`
41
- flex-shrink: 1;
42
- display: flex;
43
- justify-content: space-between;
44
- align-items: center;
45
-
46
- .tooltip {
47
- max-width: ${(props) => props.theme.grid.unit * 39}px;
48
- padding: ${(props) => props.theme.grid.unit * 2}px;
49
- border-radius: 6px;
50
- }
51
- `
52
-
53
40
  export const ReferenceTextField = styled(TextField)`
54
41
  padding: ${(props) => props.theme.grid.unit * 2}px
55
42
  ${(props) => props.theme.grid.unit * 3}px;
@@ -23,6 +23,12 @@ import React, { useEffect, useReducer, useState } from 'react'
23
23
  import { attrsReducer } from '../../lib/array-reducer'
24
24
  import { cleanItemValues } from '../../lib/utils'
25
25
  import { ReferencesModal, ReferencesModalProps } from './ReferencesModal'
26
+ import { getEditorProps } from '../../plugins/editor-props'
27
+ import { deleteNode, saveBibliographyItem } from '../../lib/view'
28
+ import ReactSubView from '../../views/ReactSubView'
29
+ import { EditorState, Transaction } from 'prosemirror-state'
30
+ import { EditorView } from 'prosemirror-view'
31
+ import { getBibliographyPluginState } from '../../plugins/bibliography'
26
32
 
27
33
  export type ReferencesEditorProps = Omit<
28
34
  ReferencesModalProps,
@@ -88,3 +94,35 @@ export const ReferencesEditor: React.FC<ReferencesEditorProps> = (props) => {
88
94
  />
89
95
  )
90
96
  }
97
+
98
+ export const openReferencesEditor = (
99
+ state: EditorState,
100
+ _?: (tr: Transaction) => void,
101
+ view?: EditorView
102
+ ) => {
103
+ if (!view) {
104
+ return false
105
+ }
106
+
107
+ const props = getEditorProps(state)
108
+ const bib = getBibliographyPluginState(view.state)
109
+
110
+ const componentProps: ReferencesEditorProps = {
111
+ items: bib ? Array.from(bib.bibliographyItems.values()) : [],
112
+ citationCounts: new Map<string, number>(bib?.citationCounts),
113
+ onDelete: (item) => deleteNode(view, item.id),
114
+ onSave: (item) => saveBibliographyItem(view, item),
115
+ }
116
+
117
+ const referencesEditor = ReactSubView(
118
+ props,
119
+ ReferencesEditor,
120
+ componentProps,
121
+ state.doc,
122
+ () => 0,
123
+ view
124
+ )
125
+ view.focus()
126
+ document.body.appendChild(referencesEditor)
127
+ return true
128
+ }
@@ -32,7 +32,12 @@ import {
32
32
  useScrollDetection,
33
33
  withListNavigation,
34
34
  withNavigableListItem,
35
+ ButtonGroup,
36
+ DeleteIcon,
37
+ LinkIcon,
38
+ outlineStyle,
35
39
  } from '@manuscripts/style-guide'
40
+ import { Button, IconButton } from '@manuscripts/style-guide/mui'
36
41
  import {
37
42
  BibliographyItemAttrs,
38
43
  generateNodeID,
@@ -40,11 +45,12 @@ import {
40
45
  } from '@manuscripts/transform'
41
46
  import isEqual from 'lodash/isEqual'
42
47
  import React, { useEffect, useMemo, useRef, useState } from 'react'
43
- import styled from 'styled-components'
48
+ import styled, { css } from 'styled-components'
44
49
 
45
50
  import {
46
51
  ReferenceForm,
47
52
  ReferenceFormActions,
53
+ validateReference,
48
54
  } from './ReferenceForm/ReferenceForm'
49
55
  import { ReferenceLine } from './ReferenceLine'
50
56
  import { ImportBibliographyModal } from './ImportBibliographyModal'
@@ -87,6 +93,7 @@ export const ReferencesModal: React.FC<ReferencesModalProps> = ({
87
93
 
88
94
  const [selection, setSelection] = useState<BibliographyItemAttrs>()
89
95
  const [isNew, setIsNew] = useState<boolean>(false)
96
+ const [showDeleteDialog, setShowDeleteDialog] = useState(false)
90
97
  const selectionRef = useRef<HTMLDivElement>(null)
91
98
 
92
99
  const sortedItems = useMemo(
@@ -173,6 +180,12 @@ export const ReferencesModal: React.FC<ReferencesModalProps> = ({
173
180
  return extraKeys.length > 0
174
181
  }
175
182
 
183
+ const canDeleteSelectedItem =
184
+ (selection &&
185
+ !citationCounts.get(selection.id) &&
186
+ isNewItem(selection, ['id', 'type'])) ??
187
+ false
188
+
176
189
  const handleSave = (values: BibliographyItemAttrs | undefined) => {
177
190
  if (!values || !selection) {
178
191
  return
@@ -221,8 +234,14 @@ export const ReferencesModal: React.FC<ReferencesModalProps> = ({
221
234
  setSelection(item)
222
235
  }
223
236
 
237
+ const [canSave, setCanSave] = useState(false)
238
+
224
239
  const handleChange = (values: BibliographyItemAttrs) => {
225
240
  valuesRef.current = values
241
+ const errors = validateReference(values)
242
+ const isValid = Object.keys(errors).length === 0
243
+ const dirty = actionsRef.current?.isDirty() ?? false
244
+ setCanSave(isValid && dirty)
226
245
  }
227
246
 
228
247
  const handleImportSave = (data: BibliographyItemAttrs[]) => {
@@ -257,6 +276,25 @@ export const ReferencesModal: React.FC<ReferencesModalProps> = ({
257
276
  },
258
277
  }}
259
278
  />
279
+ <Dialog
280
+ isOpen={showDeleteDialog}
281
+ category={Category.confirmation}
282
+ header="Delete Reference"
283
+ message="Are you sure you want to delete this reference from the list?"
284
+ actions={{
285
+ secondary: {
286
+ action: () => {
287
+ handleDelete()
288
+ setShowDeleteDialog(false)
289
+ },
290
+ title: 'Delete',
291
+ },
292
+ primary: {
293
+ action: () => setShowDeleteDialog(false),
294
+ title: 'Cancel',
295
+ },
296
+ }}
297
+ />
260
298
  <ReferencesModalContainer data-cy={'references-editor'}>
261
299
  <ModalHeader>
262
300
  <CloseButton data-cy="modal-close-button" onClick={onCancel} />
@@ -286,9 +324,11 @@ export const ReferencesModal: React.FC<ReferencesModalProps> = ({
286
324
  <span>New Reference</span>
287
325
  </NewReferenceButton>
288
326
  <ReferencesInnerWrapper>
289
- <ExistingReferencesHeading>
290
- Existing References
291
- </ExistingReferencesHeading>
327
+ {sortedItems.length ? (
328
+ <ExistingReferencesHeading>
329
+ Existing References
330
+ </ExistingReferencesHeading>
331
+ ) : null}
292
332
  {sortedItems.slice(startIndex, endIndex + 1).map((item) => (
293
333
  <ReferenceButton
294
334
  key={item.id}
@@ -311,6 +351,25 @@ export const ReferencesModal: React.FC<ReferencesModalProps> = ({
311
351
  showUncited={!citationCounts.get(item.id)}
312
352
  item={item}
313
353
  />
354
+ {isSelected(item) && (
355
+ <ButtonGroup>
356
+ <IconButton
357
+ component={'a'}
358
+ href={`https://doi.org/${item.DOI}`}
359
+ target={'_blank'}
360
+ size={'small'}
361
+ >
362
+ <DOILinkIcon fill={'#6E6E6E'} />
363
+ </IconButton>
364
+ <IconButton
365
+ disabled={!canDeleteSelectedItem}
366
+ size={'small'}
367
+ onClick={() => setShowDeleteDialog(true)}
368
+ >
369
+ <DeleteIcon fill={'#6E6E6E'} />
370
+ </IconButton>
371
+ </ButtonGroup>
372
+ )}
314
373
  </ReferenceButton>
315
374
  ))}
316
375
  </ReferencesInnerWrapper>
@@ -326,27 +385,32 @@ export const ReferencesModal: React.FC<ReferencesModalProps> = ({
326
385
  </ImportFromFileButton>
327
386
  </ImportFromFileFooter>
328
387
  </ReferencesSidebar>
329
- <ScrollableModalContent>
388
+ <ReferencesContent>
330
389
  {importSuccessCount !== null ? (
331
390
  <ImportSuccessPlaceholder count={importSuccessCount} />
332
391
  ) : (
333
392
  selection && (
334
393
  <ReferenceForm
335
394
  values={normalizeBlblioItem(selection)}
336
- showDelete={
337
- !citationCounts.get(selection.id) &&
338
- isNewItem(selection, ['id', 'type']) // disable the delete button for the new citations
339
- }
340
395
  onChange={handleChange}
341
- onCancel={onCancel}
342
- onDelete={handleDelete}
343
396
  onSave={handleSave}
344
397
  actionsRef={actionsRef}
345
398
  />
346
399
  )
347
400
  )}
348
- </ScrollableModalContent>
401
+ </ReferencesContent>
349
402
  </ModalBody>
403
+ <Footer>
404
+ <TertiaryButton variant="tertiary" onClick={onCancel}>
405
+ Close
406
+ </TertiaryButton>
407
+ <Button
408
+ disabled={!canSave}
409
+ onClick={() => actionsRef.current?.submit()}
410
+ >
411
+ Save Changes
412
+ </Button>
413
+ </Footer>
350
414
  </ReferencesModalContainer>
351
415
  </StyledModal>
352
416
  </>
@@ -361,8 +425,13 @@ const ReferencesSidebar = styled(ModalSidebar)`
361
425
  width: 70%;
362
426
  `
363
427
 
428
+ const ReferencesContent = styled(ScrollableModalContent)`
429
+ padding-top: 32px;
430
+ `
431
+
364
432
  const ReferencesSidebarContent = styled(SidebarContent)`
365
433
  overflow-y: auto;
434
+ padding-top: 8px;
366
435
  `
367
436
 
368
437
  const ReferencesInnerWrapper = withListNavigation(styled.div`
@@ -372,7 +441,7 @@ const ReferencesInnerWrapper = withListNavigation(styled.div`
372
441
 
373
442
  const CitationCountIconStyled = styled(CitationCountIcon)``
374
443
 
375
- const ReferenceButton = withNavigableListItem(styled.button`
444
+ const referenceButtonStyles = css`
376
445
  cursor: pointer;
377
446
  display: flex;
378
447
  width: 100%;
@@ -384,6 +453,7 @@ const ReferenceButton = withNavigableListItem(styled.button`
384
453
  border: none;
385
454
  border-radius: 0;
386
455
  text-align: inherit;
456
+ color: #353535;
387
457
 
388
458
  padding: ${(props) => props.theme.grid.unit * 4}px 0;
389
459
  border-top: 1px solid transparent;
@@ -408,6 +478,13 @@ const ReferenceButton = withNavigableListItem(styled.button`
408
478
  padding: ${(props) => props.theme.grid.unit * 2}px;
409
479
  border-radius: 6px;
410
480
  }
481
+ `
482
+
483
+ const ReferenceButton = withNavigableListItem(styled.div.attrs({
484
+ role: 'button',
485
+ tabIndex: 0,
486
+ })`
487
+ ${referenceButtonStyles}
411
488
  `)
412
489
 
413
490
  const IconContainer = styled.div`
@@ -433,10 +510,14 @@ const CitationCount = styled.div`
433
510
  }
434
511
  `
435
512
 
436
- const NewReferenceButton = styled(ReferenceButton)`
513
+ const NewReferenceButton = styled.button`
514
+ ${referenceButtonStyles}
515
+
437
516
  svg {
438
517
  margin-right: 8px;
439
518
  }
519
+
520
+ ${outlineStyle}
440
521
  `
441
522
  const ExistingReferencesHeading = styled.h3`
442
523
  font-size: 18px;
@@ -474,3 +555,24 @@ const ImportFromFileButton = styled.button`
474
555
  fill: #6e6e6e;
475
556
  }
476
557
  `
558
+
559
+ const Footer = styled.div`
560
+ display: flex;
561
+ justify-content: flex-end;
562
+ padding: 16px 32px;
563
+ gap: 16px;
564
+ `
565
+
566
+ const TertiaryButton = styled(Button)`
567
+ &.MuiButton-textSecondary {
568
+ color: #0d79d0;
569
+
570
+ &:hover {
571
+ background-color: rgba(13, 121, 208, 0.04);
572
+ }
573
+ }
574
+ `
575
+
576
+ const DOILinkIcon = styled(LinkIcon)`
577
+ transform: rotate(-45deg);
578
+ `
package/src/lib/utils.ts CHANGED
@@ -33,6 +33,7 @@ import {
33
33
  import { EditorState, Selection } from 'prosemirror-state'
34
34
  import {
35
35
  ContentNodeWithPos,
36
+ findChildren,
36
37
  findChildrenByType,
37
38
  findParentNode,
38
39
  findParentNodeOfTypeClosestToPos,
@@ -332,3 +333,26 @@ export const getLastTitleNode = (state: ManuscriptEditorState) => {
332
333
  const titleNode = findChildrenByType(state.doc, state.schema.nodes.title)[0]
333
334
  return titleNode
334
335
  }
336
+
337
+ export const getSurroundingText = (pos: ResolvedPos, node: ManuscriptNode) => {
338
+ const nodeContent = findChildren(
339
+ pos.node(),
340
+ (child) => child.attrs.id === node.attrs.id
341
+ )[0]
342
+
343
+ const content = pos.node().content
344
+ const MAX_LENGTH = 40
345
+
346
+ const nodeStart = nodeContent.pos
347
+ const nodeEnd = nodeContent.pos + node.nodeSize
348
+
349
+ const leftHandText = content.textBetween(
350
+ nodeStart > MAX_LENGTH ? nodeStart - MAX_LENGTH : 0,
351
+ nodeStart
352
+ )
353
+ const rightHandText = content.textBetween(
354
+ nodeEnd,
355
+ Math.min(nodeEnd + MAX_LENGTH, content.size)
356
+ )
357
+ return { leftHandText, rightHandText }
358
+ }
package/src/lib/view.ts CHANGED
@@ -180,3 +180,4 @@ export const isSelectionInsideNode = (
180
180
 
181
181
  return from >= pos && to <= end
182
182
  }
183
+
package/src/menus.tsx CHANGED
@@ -66,6 +66,7 @@ import { isEditAllowed } from './lib/utils'
66
66
  import { getEditorProps } from './plugins/editor-props'
67
67
  import { useEditor } from './useEditor'
68
68
  import { openInsertAwardModal } from './components/awards/AwardModal'
69
+ import { openReferencesEditor } from './components/references/ReferencesEditor'
69
70
 
70
71
  export const getEditorMenus = (
71
72
  editor: ReturnType<typeof useEditor>
@@ -652,6 +653,17 @@ export const getEditorMenus = (
652
653
  run: doCommand(insertInlineCitation),
653
654
  isHidden: !templateAllows(state, schema.nodes.citation),
654
655
  },
656
+ {
657
+ id: 'insert-reference-section',
658
+ label: 'New reference',
659
+ shortcut: {
660
+ mac: 'Option+CommandOrControl+N',
661
+ pc: 'CommandOrControl+Option+N',
662
+ },
663
+ isEnabled: isEditAllowed(state),
664
+ run: doCommand(openReferencesEditor),
665
+ isHidden: !templateAllows(state, schema.nodes.bibliography_item),
666
+ },
655
667
  {
656
668
  id: 'insert-cross-reference',
657
669
  label: 'Cross-reference',
@@ -20,18 +20,28 @@ import {
20
20
  schema,
21
21
  Target,
22
22
  } from '@manuscripts/transform'
23
- import { trackChangesPluginKey } from '@manuscripts/track-changes-plugin'
23
+ import {
24
+ trackChangesPluginKey,
25
+ TrackChangesAction,
26
+ } from '@manuscripts/track-changes-plugin'
24
27
  import isEqual from 'lodash/isEqual'
25
28
  import { Node, ResolvedPos } from 'prosemirror-model'
26
29
  import { NodeSelection, Plugin, Transaction } from 'prosemirror-state'
27
30
  import { Decoration, DecorationSet, EditorView } from 'prosemirror-view'
28
31
 
29
- import { XrefGroup } from '../components/cross-ref-check-modal/CrossRefWarningModal'
32
+ import {
33
+ DeleteOption,
34
+ XrefGroup,
35
+ } from '../components/cross-ref-check-modal/CrossRefWarningModal'
30
36
  import { openCrossRefWarningModal } from '../components/cross-ref-check-modal/openModal'
31
37
  import { objectsKey } from './objects'
32
38
 
39
+ export const DELETE_WITHOUT_REF = 'delete-without-ref'
40
+ export const SELECTED_CROSS_REF_CLASS = 'selected-cross-reference'
41
+
33
42
  let modalActive = false
34
43
  let modalElement: HTMLDivElement | null = null
44
+ let scrollTargetEl: HTMLElement | null = null
35
45
 
36
46
  export default () => {
37
47
  let view: ManuscriptEditorView | null = null
@@ -55,7 +65,10 @@ export default () => {
55
65
  !view ||
56
66
  !tr.docChanged ||
57
67
  tr.getMeta(trackChangesPluginKey) ||
58
- tr.getMeta('addToHistory') === false
68
+ tr.getMeta(TrackChangesAction.refreshChanges) ||
69
+ tr.getMeta('addToHistory') === false ||
70
+ tr.getMeta('collab$') ||
71
+ tr.getMeta(DELETE_WITHOUT_REF)
59
72
  ) {
60
73
  return true
61
74
  }
@@ -81,6 +94,9 @@ export default () => {
81
94
  modalElement.remove()
82
95
  modalElement = null
83
96
  }
97
+ if (scrollTargetEl) {
98
+ scrollTargetEl.classList.remove(SELECTED_CROSS_REF_CLASS)
99
+ }
84
100
  }
85
101
 
86
102
  const deletedIds = new Set(
@@ -206,7 +222,8 @@ function createXrefGroups(
206
222
  const xrefs = xrefsByRid.get(id)
207
223
  if (xrefs?.length) {
208
224
  const label = targets.get(referenced.attrs.id)?.label || ''
209
- xrefGroups.push({ referenced, label, xrefs })
225
+ const caption = targets.get(referenced.attrs.id)?.caption || ''
226
+ xrefGroups.push({ referenced, label, caption, xrefs })
210
227
  }
211
228
  }
212
229
  return xrefGroups
@@ -219,7 +236,7 @@ const onConfirmCreator =
219
236
  deletedIds: Set<string>,
220
237
  tr: Transaction
221
238
  ) =>
222
- () => {
239
+ (deleteOption: DeleteOption) => {
223
240
  cleanup()
224
241
  if (!view) {
225
242
  return
@@ -237,21 +254,25 @@ const onConfirmCreator =
237
254
  }
238
255
  newTr.step(step)
239
256
  }
240
- // Remove cross-references that pointed to the now-deleted nodes.
241
- // Collect positions in reverse order so deletions don't shift
242
- // positions of earlier entries.
243
- const xrefPositions: { from: number; to: number }[] = []
257
+ if (deleteOption === 'delete-with-ref') {
258
+ // Remove cross-references that pointed to the now-deleted nodes.
259
+ // Collect positions in reverse order so deletions don't shift
260
+ // positions of earlier entries.
261
+ const xrefPositions: { from: number; to: number }[] = []
244
262
  newTr.doc.descendants((node, pos) => {
245
263
  if (node.type === schema.nodes.cross_reference) {
246
264
  const rids = node.attrs.rids as string[]
247
265
  if (rids.some((rid) => deletedIds.has(rid))) {
248
266
  xrefPositions.push({ from: pos, to: pos + node.nodeSize })
249
267
  }
250
- }
268
+ }
251
269
  })
252
270
  for (let i = xrefPositions.length - 1; i >= 0; i--) {
253
- const { from, to } = xrefPositions[i]
254
- newTr.delete(from, to)
271
+ const { from, to } = xrefPositions[i]
272
+ newTr.delete(from, to)
273
+ }
274
+ } else {
275
+ newTr.setMeta(DELETE_WITHOUT_REF, true)
255
276
  }
256
277
  view.dispatch(newTr)
257
278
  }
@@ -263,7 +284,6 @@ const selectAndScrollToCreator = (view: EditorView) => ($pos: ResolvedPos) => {
263
284
 
264
285
  const selTr = view.state.tr
265
286
  selTr.setSelection(NodeSelection.create(view.state.doc, $pos.pos))
266
- view.focus()
267
287
  view.dispatch(selTr)
268
288
  // Standard PM's scrollIntoView doesn't allow placement control - hence switching to native DOM's peer method.
269
289
  let scrollable = view.dom.parentElement
@@ -293,4 +313,12 @@ const selectAndScrollToCreator = (view: EditorView) => ($pos: ResolvedPos) => {
293
313
  modalElement?.classList.remove('modal-bottom')
294
314
  scrollable.scrollTo({ top: scrollTo, behavior: 'smooth' })
295
315
  }
316
+ const dom = view.nodeDOM($pos.pos)
317
+ if (dom && dom instanceof HTMLElement) {
318
+ dom.classList.add(SELECTED_CROSS_REF_CLASS)
319
+ if (scrollTargetEl && scrollTargetEl !== dom) {
320
+ scrollTargetEl?.classList.remove(SELECTED_CROSS_REF_CLASS)
321
+ }
322
+ scrollTargetEl = dom
323
+ }
296
324
  }
package/src/versions.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  //THIS FILE WAS GENERATED BY versions.mjs
2
- export const VERSION = '3.19.0';
2
+ export const VERSION = '3.19.2';
3
3
  export const MATHJAX_VERSION = '3.2.2';
@@ -24,6 +24,8 @@ import { ThemeProvider } from 'styled-components'
24
24
  import { EditorProps } from '../configs/ManuscriptsEditor'
25
25
  import { Trackable } from '../types'
26
26
 
27
+ export type SubViewContainer = HTMLDivElement & { unmount: () => void }
28
+
27
29
  export interface ReactViewComponentProps<NodeT extends ManuscriptNode> {
28
30
  nodeAttrs: NodeT['attrs']
29
31
  setNodeAttrs: (nextAttrs: Partial<NodeT['attrs']>) => void
@@ -32,7 +34,7 @@ export interface ReactViewComponentProps<NodeT extends ManuscriptNode> {
32
34
  getPos: () => number
33
35
  node: ManuscriptNode | Trackable<ManuscriptNode>
34
36
  }
35
- container: HTMLDivElement
37
+ container: SubViewContainer
36
38
  }
37
39
  /*
38
40
  This is to render components that affect the Prosemirror Document indirectly. Such as dropdown buttons, inputs, advanced UX elements etc.
@@ -46,8 +48,9 @@ function createSubView<T extends Trackable<ManuscriptNode>>(
46
48
  getPos: () => number,
47
49
  view: ManuscriptEditorView,
48
50
  classNames: string[] = []
49
- ): HTMLDivElement {
50
- const container = document.createElement('div')
51
+ ): SubViewContainer {
52
+ const container = document.createElement('div') as SubViewContainer
53
+ container.unmount = () => root.unmount()
51
54
  const Wrapped = createView<T>(
52
55
  props,
53
56
  Component,
@@ -50,11 +50,17 @@ export class CrossReferenceView
50
50
 
51
51
  const isSupplement = target?.type === schema.nodes.supplement.name
52
52
  const derivedLabel = isSupplement
53
- ? target?.caption || target?.label || ''
54
- : target?.label || ''
53
+ ? target?.caption || target?.label || '[cross-ref]'
54
+ : target?.label || '[cross-ref]'
55
55
 
56
56
  this.dom.textContent = attrs.label || derivedLabel
57
57
  this.dom.addEventListener('click', this.handleClick)
58
+
59
+ if (this.isOrphaned()) {
60
+ this.dom.classList.add('orphaned-cross-reference')
61
+ } else {
62
+ this.dom.classList.remove('orphaned-cross-reference')
63
+ }
58
64
  }
59
65
 
60
66
  public initialise = () => {
@@ -74,6 +80,12 @@ export class CrossReferenceView
74
80
  handleEnterKey(() => this.handleClick())
75
81
  )
76
82
  }
83
+
84
+ protected isOrphaned() {
85
+ const targets = objectsKey.getState(this.view.state) as Map<string, Target>
86
+ const rid = this.node.attrs.rids[0]
87
+ return !targets?.get(rid)
88
+ }
77
89
  }
78
90
 
79
91
  export default createNodeView(CrossReferenceView)
@@ -147,9 +147,7 @@ export class CrossReferenceEditableView extends CrossReferenceView {
147
147
 
148
148
  const can = this.props.getCapabilities()
149
149
 
150
- const targets = objectsKey.getState(this.view.state) as Map<string, Target>
151
- const rid = this.node.attrs.rids[0]
152
- const isOrphaned = !targets?.get(rid)
150
+ const isOrphaned = this.isOrphaned()
153
151
 
154
152
  const actions: ContextMenuProps['actions'] = [
155
153
  {
@@ -173,7 +171,6 @@ export class CrossReferenceEditableView extends CrossReferenceView {
173
171
  label: 'Edit',
174
172
  icon: 'Edit',
175
173
  action: () => this.handleEdit(),
176
- disabled: isOrphaned,
177
174
  })
178
175
  }
179
176
 
package/styles/Editor.css CHANGED
@@ -300,12 +300,24 @@
300
300
  color: inherit;
301
301
  }
302
302
 
303
+ .ProseMirror .orphaned-cross-reference {
304
+ text-decoration: underline dotted #f35143;
305
+ }
306
+
303
307
  .ProseMirror .bib-item.ProseMirror-selectednode,
304
308
  .ProseMirror .citation.ProseMirror-selectednode,
305
309
  .ProseMirror .cross-reference.ProseMirror-selectednode {
306
310
  background-color: #bce7f6;
307
311
  }
308
312
 
313
+ .ProseMirror .cross-reference.selected-cross-reference {
314
+ border-radius: 4px;
315
+ outline: 2px solid #FE8F1F;
316
+ outline-offset: 1.5px;
317
+ background: #FFF9E5;
318
+ mix-blend-mode: darken;
319
+ }
320
+
309
321
  .ProseMirror .block-table_element .block-gutter {
310
322
  padding-top: 1em;
311
323
  }