@manuscripts/body-editor 3.9.11 → 3.9.13

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 (105) hide show
  1. package/dist/cjs/commands.js +5 -2
  2. package/dist/cjs/components/keywords/AddKeywordInline.js +12 -1
  3. package/dist/cjs/components/views/FigureDropdown.js +65 -6
  4. package/dist/cjs/configs/ManuscriptsEditor.js +1 -1
  5. package/dist/cjs/index.js +1 -0
  6. package/dist/cjs/keys/misc.js +2 -1
  7. package/dist/cjs/keys/title.js +0 -38
  8. package/dist/cjs/lib/comments.js +1 -0
  9. package/dist/cjs/lib/context-menu.js +74 -8
  10. package/dist/cjs/lib/media.js +27 -3
  11. package/dist/cjs/lib/navigation-utils.js +132 -0
  12. package/dist/cjs/lib/popper.js +25 -1
  13. package/dist/cjs/lib/position-menu.js +3 -0
  14. package/dist/cjs/lib/utils.js +7 -2
  15. package/dist/cjs/plugins/accessibility_element.js +10 -2
  16. package/dist/cjs/plugins/add-subtitle.js +8 -2
  17. package/dist/cjs/plugins/alt-titles.js +6 -1
  18. package/dist/cjs/plugins/comments.js +27 -15
  19. package/dist/cjs/plugins/persistent-cursor.js +4 -6
  20. package/dist/cjs/plugins/section_category.js +42 -9
  21. package/dist/cjs/plugins/translations.js +49 -13
  22. package/dist/cjs/versions.js +1 -1
  23. package/dist/cjs/views/accessibility_element.js +30 -0
  24. package/dist/cjs/views/alt_title.js +29 -0
  25. package/dist/cjs/views/alt_titles_section.js +9 -1
  26. package/dist/cjs/views/attachment.js +1 -1
  27. package/dist/cjs/views/bibliography_element.js +39 -17
  28. package/dist/cjs/views/citation.js +1 -0
  29. package/dist/cjs/views/citation_editable.js +4 -2
  30. package/dist/cjs/views/contributors.js +23 -2
  31. package/dist/cjs/views/cross_reference.js +3 -0
  32. package/dist/cjs/views/editable_block.js +37 -3
  33. package/dist/cjs/views/embed.js +3 -3
  34. package/dist/cjs/views/figure_editable.js +1 -1
  35. package/dist/cjs/views/figure_element.js +3 -0
  36. package/dist/cjs/views/footnote.js +3 -0
  37. package/dist/cjs/views/hero_image.js +4 -1
  38. package/dist/cjs/views/image_element.js +15 -7
  39. package/dist/cjs/views/inline_footnote.js +3 -0
  40. package/dist/cjs/views/keyword.js +15 -0
  41. package/dist/cjs/views/keyword_group.js +38 -0
  42. package/dist/cjs/views/quote_image_editable.js +1 -0
  43. package/dist/cjs/views/supplements.js +4 -1
  44. package/dist/es/commands.js +5 -2
  45. package/dist/es/components/keywords/AddKeywordInline.js +12 -1
  46. package/dist/es/components/views/FigureDropdown.js +66 -7
  47. package/dist/es/configs/ManuscriptsEditor.js +1 -1
  48. package/dist/es/index.js +1 -0
  49. package/dist/es/keys/misc.js +2 -1
  50. package/dist/es/keys/title.js +1 -39
  51. package/dist/es/lib/comments.js +1 -0
  52. package/dist/es/lib/context-menu.js +74 -8
  53. package/dist/es/lib/media.js +27 -3
  54. package/dist/es/lib/navigation-utils.js +122 -0
  55. package/dist/es/lib/popper.js +25 -1
  56. package/dist/es/lib/position-menu.js +3 -0
  57. package/dist/es/lib/utils.js +7 -2
  58. package/dist/es/plugins/accessibility_element.js +10 -2
  59. package/dist/es/plugins/add-subtitle.js +8 -2
  60. package/dist/es/plugins/alt-titles.js +6 -1
  61. package/dist/es/plugins/comments.js +27 -15
  62. package/dist/es/plugins/persistent-cursor.js +4 -6
  63. package/dist/es/plugins/section_category.js +42 -9
  64. package/dist/es/plugins/translations.js +49 -13
  65. package/dist/es/versions.js +1 -1
  66. package/dist/es/views/accessibility_element.js +30 -0
  67. package/dist/es/views/alt_title.js +29 -0
  68. package/dist/es/views/alt_titles_section.js +9 -1
  69. package/dist/es/views/attachment.js +1 -1
  70. package/dist/es/views/bibliography_element.js +39 -17
  71. package/dist/es/views/citation.js +1 -0
  72. package/dist/es/views/citation_editable.js +4 -2
  73. package/dist/es/views/contributors.js +23 -2
  74. package/dist/es/views/cross_reference.js +3 -0
  75. package/dist/es/views/editable_block.js +37 -3
  76. package/dist/es/views/embed.js +3 -3
  77. package/dist/es/views/figure_editable.js +1 -1
  78. package/dist/es/views/figure_element.js +3 -0
  79. package/dist/es/views/footnote.js +3 -0
  80. package/dist/es/views/hero_image.js +4 -1
  81. package/dist/es/views/image_element.js +15 -7
  82. package/dist/es/views/inline_footnote.js +3 -0
  83. package/dist/es/views/keyword.js +15 -0
  84. package/dist/es/views/keyword_group.js +38 -0
  85. package/dist/es/views/quote_image_editable.js +1 -0
  86. package/dist/es/views/supplements.js +4 -1
  87. package/dist/types/configs/ManuscriptsEditor.d.ts +1 -1
  88. package/dist/types/index.d.ts +1 -0
  89. package/dist/types/lib/context-menu.d.ts +1 -0
  90. package/dist/types/lib/media.d.ts +1 -1
  91. package/dist/types/lib/navigation-utils.d.ts +45 -0
  92. package/dist/types/lib/popper.d.ts +3 -0
  93. package/dist/types/lib/utils.d.ts +1 -1
  94. package/dist/types/versions.d.ts +1 -1
  95. package/dist/types/views/accessibility_element.d.ts +2 -0
  96. package/dist/types/views/alt_title.d.ts +2 -0
  97. package/dist/types/views/bibliography_element.d.ts +3 -0
  98. package/dist/types/views/citation_editable.d.ts +1 -1
  99. package/dist/types/views/contributors.d.ts +3 -1
  100. package/dist/types/views/keyword.d.ts +1 -0
  101. package/dist/types/views/keyword_group.d.ts +4 -0
  102. package/package.json +4 -4
  103. package/styles/AdvancedEditor.css +116 -10
  104. package/styles/Editor.css +61 -6
  105. package/styles/popper.css +3 -1
@@ -19,6 +19,7 @@ import { NodeSelection } from 'prosemirror-state';
19
19
  import { AuthorsModal, } from '../components/authors/AuthorsModal';
20
20
  import { authorComparator, authorLabel, } from '../lib/authors';
21
21
  import { handleComment } from '../lib/comments';
22
+ import { createKeyboardInteraction } from '../lib/navigation-utils';
22
23
  import { addTrackChangesAttributes, isDeleted, } from '../lib/track-changes-utils';
23
24
  import { findInsertionPosition } from '../lib/utils';
24
25
  import { deleteNode, findChildByID, findChildByType, findChildrenAttrsByType, updateNodeAttrs, } from '../lib/view';
@@ -45,11 +46,26 @@ export class ContributorsView extends BlockView {
45
46
  const can = this.props.getCapabilities();
46
47
  if (can.editMetadata) {
47
48
  wrapper.addEventListener('click', this.handleClick);
49
+ this.removeKeydownListener = createKeyboardInteraction({
50
+ container: wrapper,
51
+ navigation: {
52
+ getItems: () => {
53
+ return Array.from(wrapper.querySelectorAll('.contributor'));
54
+ },
55
+ arrowKeys: { forward: 'ArrowRight', backward: 'ArrowLeft' },
56
+ },
57
+ additionalKeys: {
58
+ Enter: (e) => {
59
+ e.preventDefault();
60
+ this.handleClick(e);
61
+ },
62
+ },
63
+ });
48
64
  }
49
65
  const authors = affs.contributors;
50
66
  authors.sort(authorComparator).forEach((author, i) => {
51
67
  const jointAuthors = this.isJointFirstAuthor(authors, i);
52
- wrapper.appendChild(this.buildAuthor(author, jointAuthors));
68
+ wrapper.appendChild(this.buildAuthor(author, jointAuthors, i));
53
69
  if (i !== authors.length - 1) {
54
70
  const separator = document.createElement('span');
55
71
  separator.classList.add('separator');
@@ -59,13 +75,14 @@ export class ContributorsView extends BlockView {
59
75
  });
60
76
  this.container.appendChild(wrapper);
61
77
  };
62
- this.buildAuthor = (attrs, isJointFirstAuthor) => {
78
+ this.buildAuthor = (attrs, isJointFirstAuthor, index) => {
63
79
  const state = this.view.state;
64
80
  const affs = affiliationsKey.getState(state)?.indexedAffiliationIds;
65
81
  const container = document.createElement('span');
66
82
  container.classList.add('contributor');
67
83
  container.setAttribute('id', attrs.id);
68
84
  container.setAttribute('contenteditable', 'false');
85
+ container.tabIndex = index === 0 ? 0 : -1;
69
86
  addTrackChangesAttributes(attrs, container);
70
87
  const name = authorLabel(attrs);
71
88
  container.innerHTML =
@@ -269,5 +286,9 @@ export class ContributorsView extends BlockView {
269
286
  note.classList.add('contributor-note');
270
287
  return note;
271
288
  }
289
+ destroy() {
290
+ this.removeKeydownListener?.();
291
+ super.destroy();
292
+ }
272
293
  }
273
294
  export default createNodeView(ContributorsView);
@@ -13,6 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+ import { handleEnterKey } from '../lib/navigation-utils';
16
17
  import { objectsKey } from '../plugins/objects';
17
18
  import { BaseNodeView } from './base_node_view';
18
19
  import { createNodeView } from './creators';
@@ -37,6 +38,8 @@ export class CrossReferenceView extends BaseNodeView {
37
38
  this.createDOM = () => {
38
39
  this.dom = document.createElement('span');
39
40
  this.dom.className = 'cross-reference';
41
+ this.dom.tabIndex = 0;
42
+ this.dom.addEventListener('keydown', handleEnterKey(() => this.handleClick()));
40
43
  };
41
44
  }
42
45
  updateContents() {
@@ -16,6 +16,7 @@
16
16
  import { schema } from '@manuscripts/transform';
17
17
  import { ContextMenu, contextMenuBtnClass } from '../lib/context-menu';
18
18
  import { hasParent, isNotNull } from '../lib/utils';
19
+ import { createKeyboardInteraction } from '../lib/navigation-utils';
19
20
  export const EditableBlock = (Base) => {
20
21
  return class extends Base {
21
22
  constructor() {
@@ -25,7 +26,12 @@ export const EditableBlock = (Base) => {
25
26
  };
26
27
  }
27
28
  gutterButtons() {
28
- return [this.createAddButton(), this.createEditButton()].filter(isNotNull);
29
+ const buttons = [this.createAddButton(), this.createEditButton()].filter(isNotNull);
30
+ buttons.forEach((btn) => (btn.tabIndex = -1));
31
+ if (buttons.length > 0) {
32
+ buttons[0].tabIndex = 0;
33
+ }
34
+ return buttons;
29
35
  }
30
36
  actionGutterButtons() {
31
37
  return [];
@@ -53,10 +59,24 @@ export const EditableBlock = (Base) => {
53
59
  button.setAttribute('role', 'button');
54
60
  button.setAttribute('aria-label', `Add an element below`);
55
61
  button.setAttribute('data-balloon-pos', 'down-left');
56
- button.addEventListener('mousedown', (event) => {
62
+ const handleClick = (event) => {
57
63
  event.preventDefault();
58
64
  const menu = this.createMenu();
59
65
  menu.showAddMenu(event.currentTarget);
66
+ };
67
+ button.addEventListener('mousedown', handleClick);
68
+ createKeyboardInteraction({
69
+ container: button,
70
+ additionalKeys: {
71
+ Enter: handleClick,
72
+ ArrowRight: () => {
73
+ const parent = button.parentElement;
74
+ const editButton = parent?.querySelector('.edit-block');
75
+ if (editButton) {
76
+ editButton.focus();
77
+ }
78
+ },
79
+ },
60
80
  });
61
81
  return button;
62
82
  }
@@ -69,10 +89,24 @@ export const EditableBlock = (Base) => {
69
89
  button.setAttribute('role', 'button');
70
90
  button.setAttribute('aria-label', 'Open menu');
71
91
  button.setAttribute('data-balloon-pos', 'down-left');
72
- button.addEventListener('mousedown', (event) => {
92
+ const handleClick = (event) => {
73
93
  event.preventDefault();
74
94
  const menu = this.createMenu();
75
95
  menu.showEditMenu(event.currentTarget);
96
+ };
97
+ button.addEventListener('mousedown', handleClick);
98
+ createKeyboardInteraction({
99
+ container: button,
100
+ additionalKeys: {
101
+ Enter: handleClick,
102
+ ArrowLeft: () => {
103
+ const parent = button.parentElement;
104
+ const addButton = parent?.querySelector('.add-block');
105
+ if (addButton) {
106
+ addButton.focus();
107
+ }
108
+ },
109
+ },
76
110
  });
77
111
  return button;
78
112
  }
@@ -158,7 +158,7 @@ export class EmbedView extends BlockView {
158
158
  const href = this.node.attrs.href;
159
159
  let object;
160
160
  if (!href) {
161
- object = createMediaPlaceholder(MediaType.Media, this.view, this.getPos);
161
+ object = createMediaPlaceholder(MediaType.Media, this.view, this.getPos, this.props);
162
162
  }
163
163
  else if (this.isUploadedFile()) {
164
164
  const files = this.props.getFiles();
@@ -172,14 +172,14 @@ export class EmbedView extends BlockView {
172
172
  : createUnsupportedFormat(file.name, this.props.getCapabilities().editArticle);
173
173
  }
174
174
  else {
175
- object = createMediaPlaceholder(MediaType.Media, this.view, this.getPos);
175
+ object = createMediaPlaceholder(MediaType.Media, this.view, this.getPos, this.props);
176
176
  }
177
177
  }
178
178
  else if (this.isEmbedLink()) {
179
179
  object = await this.createEmbedPreview();
180
180
  }
181
181
  else {
182
- object = createMediaPlaceholder(MediaType.Media, this.view, this.getPos);
182
+ object = createMediaPlaceholder(MediaType.Media, this.view, this.getPos, this.props);
183
183
  }
184
184
  const can = this.props.getCapabilities();
185
185
  if (can.uploadFile && object.classList.contains('placeholder')) {
@@ -49,7 +49,7 @@ export class FigureEditableView extends FigureView {
49
49
  return img;
50
50
  };
51
51
  this.createPlaceholder = () => {
52
- return createMediaPlaceholder(MediaType.Figure);
52
+ return createMediaPlaceholder(MediaType.Figure, this.view, this.getPos, this.props);
53
53
  };
54
54
  }
55
55
  initialise() {
@@ -16,6 +16,7 @@
16
16
  import { schema } from '@manuscripts/transform';
17
17
  import { TextSelection } from 'prosemirror-state';
18
18
  import { addAuthorIcon } from '../icons';
19
+ import { handleEnterKey } from '../lib/navigation-utils';
19
20
  import { createNodeView } from './creators';
20
21
  import { ImageElementView } from './image_element';
21
22
  export class FigureElementView extends ImageElementView {
@@ -71,6 +72,8 @@ export class FigureElementView extends ImageElementView {
71
72
  title: 'Add figure',
72
73
  });
73
74
  this.addFigureBtn.addEventListener('click', () => this.addFigure());
75
+ this.addFigureBtn.addEventListener('keydown', handleEnterKey(() => this.addFigure()));
76
+ this.addFigureBtn.tabIndex = 0;
74
77
  this.container.prepend(this.addFigureBtn);
75
78
  }
76
79
  }
@@ -25,15 +25,18 @@ import { isDeleted, isPendingInsert } from '../lib/track-changes-utils';
25
25
  import { BaseNodeView } from './base_node_view';
26
26
  import { createNodeView } from './creators';
27
27
  import ReactSubView from './ReactSubView';
28
+ import { handleEnterKey } from '../lib/navigation-utils';
28
29
  export class FootnoteView extends BaseNodeView {
29
30
  constructor() {
30
31
  super(...arguments);
31
32
  this.initialise = () => {
32
33
  this.dom = document.createElement('div');
33
34
  this.dom.classList.add('footnote');
35
+ this.dom.tabIndex = 0;
34
36
  this.contentDOM = document.createElement('div');
35
37
  this.contentDOM.classList.add('footnote-text');
36
38
  this.dom.addEventListener('mousedown', this.handleClick);
39
+ this.dom.addEventListener('keydown', handleEnterKey(this.handleClick));
37
40
  this.updateContents();
38
41
  };
39
42
  this.handleClick = (event) => {
@@ -14,6 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { arrowUp } from '../icons';
17
+ import { handleEnterKey } from '../lib/navigation-utils';
17
18
  import BlockView from './block_view';
18
19
  import { createNodeView } from './creators';
19
20
  export class HeroImageView extends BlockView {
@@ -42,13 +43,15 @@ export class HeroImageView extends BlockView {
42
43
  heroImageToggleBtn.classList.add('toggle-btn', 'button-reset');
43
44
  heroImageToggleBtn.innerHTML = arrowUp;
44
45
  heroImageToggleBtn.classList.toggle('collapsed', this.collapsed);
45
- heroImageToggleBtn.onclick = () => {
46
+ const handleToggle = () => {
46
47
  this.collapsed = !this.collapsed;
47
48
  if (this.contentDOM) {
48
49
  this.contentDOM.style.display = this.collapsed ? 'none' : '';
49
50
  }
50
51
  heroImageToggleBtn.classList.toggle('collapsed', this.collapsed);
51
52
  };
53
+ heroImageToggleBtn.onclick = handleToggle;
54
+ heroImageToggleBtn.addEventListener('keydown', handleEnterKey(handleToggle));
52
55
  panel.appendChild(label);
53
56
  panel.appendChild(heroImageToggleBtn);
54
57
  return panel;
@@ -17,6 +17,7 @@ import { ContextMenu } from '@manuscripts/style-guide';
17
17
  import { schema } from '@manuscripts/transform';
18
18
  import { deleteIcon, linkIcon } from '../icons';
19
19
  import { addInteractionHandlers, createMediaPlaceholder, MediaType, } from '../lib/media';
20
+ import { handleEnterKey } from '../lib/navigation-utils';
20
21
  import { createPositionMenuWrapper } from '../lib/position-menu';
21
22
  import BlockView from './block_view';
22
23
  import { createNodeView } from './creators';
@@ -194,14 +195,16 @@ export class ImageElementView extends BlockView {
194
195
  label.innerText = 'Link';
195
196
  const container = document.createElement('div');
196
197
  container.classList.add('ext-link-editor-placeholder-container');
197
- const placeholder = createMediaPlaceholder(MediaType.ExternalLink);
198
+ const placeholder = createMediaPlaceholder(MediaType.ExternalLink, this.view, this.getPos, this.props);
198
199
  const closeButton = document.createElement('button');
199
200
  closeButton.classList.add('close-button');
200
201
  closeButton.setAttribute('aria-label', 'Close');
201
- closeButton.addEventListener('click', () => {
202
+ const handleClose = () => {
202
203
  this.setIsEditingExtLink(false);
203
204
  this.updateContents();
204
- });
205
+ };
206
+ closeButton.addEventListener('click', handleClose);
207
+ closeButton.addEventListener('keydown', handleEnterKey(handleClose));
205
208
  container.append(placeholder, closeButton);
206
209
  this.extLinkEditorContainer.append(label, container);
207
210
  if (can.uploadFile) {
@@ -216,10 +219,13 @@ export class ImageElementView extends BlockView {
216
219
  button.appendChild(buttonText);
217
220
  button.setAttribute('aria-label', 'Add linked file');
218
221
  button.classList.add('icon-button');
219
- button.addEventListener('click', () => {
222
+ button.tabIndex = 0;
223
+ const handleAdd = () => {
220
224
  this.setIsEditingExtLink(true);
221
225
  this.updateContents();
222
- });
226
+ };
227
+ button.addEventListener('click', handleAdd);
228
+ button.addEventListener('keydown', handleEnterKey(handleAdd));
223
229
  this.extLinkEditorContainer.appendChild(button);
224
230
  }
225
231
  createLinkedFile() {
@@ -234,10 +240,12 @@ export class ImageElementView extends BlockView {
234
240
  removeButton.classList.add('icon-button', 'remove-button');
235
241
  removeButton.setAttribute('aria-label', 'Remove link');
236
242
  removeButton.innerHTML = deleteIcon;
237
- removeButton.addEventListener('click', () => {
243
+ const handleRemove = () => {
238
244
  this.isEditingExtLink = false;
239
245
  this.removeExtLink();
240
- });
246
+ };
247
+ removeButton.addEventListener('click', handleRemove);
248
+ removeButton.addEventListener('keydown', handleEnterKey(handleRemove));
241
249
  div.appendChild(removeButton);
242
250
  this.extLinkEditorContainer.appendChild(div);
243
251
  }
@@ -17,6 +17,7 @@ import { ContextMenu } from '@manuscripts/style-guide';
17
17
  import { NodeSelection, TextSelection } from 'prosemirror-state';
18
18
  import { insertFootnotesElement } from '../commands';
19
19
  import { FootnotesSelector, } from '../components/views/FootnotesSelector';
20
+ import { handleEnterKey } from '../lib/navigation-utils';
20
21
  import { createFootnote, findFootnotesContainerNode, getFootnotesElementState, } from '../lib/footnotes';
21
22
  import { isDeleted, isPendingInsert } from '../lib/track-changes-utils';
22
23
  import { BaseNodeView } from './base_node_view';
@@ -102,7 +103,9 @@ export class InlineFootnoteView extends BaseNodeView {
102
103
  this.initialise = () => {
103
104
  this.dom = this.createDOM();
104
105
  this.dom.classList.add('footnote-marker');
106
+ this.dom.tabIndex = 0;
105
107
  this.dom.addEventListener('click', this.handleClick);
108
+ this.dom.addEventListener('keydown', handleEnterKey(() => this.handleClick()));
106
109
  this.updateContents();
107
110
  };
108
111
  this.selectNode = () => {
@@ -15,9 +15,11 @@
15
15
  */
16
16
  import { TextSelection } from 'prosemirror-state';
17
17
  import { DeleteKeywordDialog, } from '../components/keywords/DeleteKeywordDialog';
18
+ import { handleEnterKey } from '../lib/navigation-utils';
18
19
  import { BaseNodeView } from './base_node_view';
19
20
  import { createNodeView } from './creators';
20
21
  import ReactSubView from './ReactSubView';
22
+ import { isDeleted } from '../lib/track-changes-utils';
21
23
  const deleteIcon = '<svg width="8px" height="8px" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg">\n' +
22
24
  ' <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">\n' +
23
25
  ' <g fill="#6E6E6E">\n' +
@@ -36,6 +38,8 @@ export class KeywordView extends BaseNodeView {
36
38
  this.createDOM = () => {
37
39
  this.dom = document.createElement('span');
38
40
  this.dom.classList.add('keyword');
41
+ this.dom.tabIndex = this.isFirstKeyword() ? 0 : -1;
42
+ this.dom.addEventListener('keydown', handleEnterKey(() => this.showConfirmationDialog()));
39
43
  this.contentDOM = document.createElement('span');
40
44
  };
41
45
  this.showConfirmationDialog = () => {
@@ -57,6 +61,17 @@ export class KeywordView extends BaseNodeView {
57
61
  }
58
62
  };
59
63
  }
64
+ isFirstKeyword() {
65
+ const pos = this.getPos();
66
+ const parent = this.view.state.doc.resolve(pos).parent;
67
+ for (let i = 0; i < parent.childCount; i++) {
68
+ const child = parent.child(i);
69
+ if (!isDeleted(child)) {
70
+ return child === this.node;
71
+ }
72
+ }
73
+ return false;
74
+ }
60
75
  updateContents() {
61
76
  super.updateContents();
62
77
  this.dom.innerHTML = '';
@@ -14,6 +14,8 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { AddKeywordInline } from '../components/keywords/AddKeywordInline';
17
+ import { createKeyboardInteraction } from '../lib/navigation-utils';
18
+ import { isDeleted } from '../lib/track-changes-utils';
17
19
  import BlockView from './block_view';
18
20
  import { createNodeView } from './creators';
19
21
  import ReactSubView from './ReactSubView';
@@ -30,6 +32,13 @@ export class KeywordGroupView extends BlockView {
30
32
  this.contentDOM.setAttribute('id', this.node.attrs.id);
31
33
  this.contentDOM.setAttribute('contenteditable', 'false');
32
34
  this.element.appendChild(this.contentDOM);
35
+ this.removeKeydownListener = createKeyboardInteraction({
36
+ container: this.element,
37
+ navigation: {
38
+ getItems: () => Array.from(this.element.querySelectorAll('.keyword:not(.deleted), .keyword-add')),
39
+ arrowKeys: { forward: 'ArrowRight', backward: 'ArrowLeft' },
40
+ },
41
+ });
33
42
  if (this.props.getCapabilities().editArticle) {
34
43
  this.addingTools = ReactSubView(this.props, AddKeywordInline, { getUpdatedNode: () => this.node }, this.node, this.getPos, this.view, ['keywords-editor']);
35
44
  }
@@ -38,5 +47,34 @@ export class KeywordGroupView extends BlockView {
38
47
  }
39
48
  };
40
49
  }
50
+ updateContents() {
51
+ super.updateContents();
52
+ this.setKeywordsTabIndices();
53
+ }
54
+ setKeywordsTabIndices() {
55
+ const container = this.contentDOM;
56
+ if (!container) {
57
+ return;
58
+ }
59
+ let firstFocusableFound = false;
60
+ for (let i = 0; i < this.node.childCount; i++) {
61
+ const child = this.node.child(i);
62
+ const keyword = container.querySelector(`.keyword[id="${child.attrs.id}"]`);
63
+ if (!keyword) {
64
+ continue;
65
+ }
66
+ if (!isDeleted(child) && !firstFocusableFound) {
67
+ keyword.tabIndex = 0;
68
+ firstFocusableFound = true;
69
+ }
70
+ else {
71
+ keyword.tabIndex = -1;
72
+ }
73
+ }
74
+ }
75
+ destroy() {
76
+ this.removeKeydownListener?.();
77
+ super.destroy();
78
+ }
41
79
  }
42
80
  export default createNodeView(KeywordGroupView);
@@ -23,6 +23,7 @@ export class QuoteImageEditableView extends FigureEditableView {
23
23
  this.createPlaceholder = () => {
24
24
  const element = document.createElement('div');
25
25
  element.classList.add('figure', 'placeholder');
26
+ element.tabIndex = 0;
26
27
  const instructions = document.createElement('div');
27
28
  instructions.classList.add('instructions');
28
29
  instructions.innerHTML = `${plusIcon}<div>Drag or click here to upload image</div>`;
@@ -14,6 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { arrowUp } from '../icons';
17
+ import { handleEnterKey } from '../lib/navigation-utils';
17
18
  import BlockView from './block_view';
18
19
  import { createNodeView } from './creators';
19
20
  export class SupplementsView extends BlockView {
@@ -25,11 +26,13 @@ export class SupplementsView extends BlockView {
25
26
  this.toggleButton = document.createElement('button');
26
27
  this.toggleButton.classList.add('supplements-toggle-btn', 'button-reset');
27
28
  this.toggleButton.innerHTML = arrowUp;
28
- this.toggleButton.onclick = () => {
29
+ const handleToggle = () => {
29
30
  this.collapsed = !this.collapsed;
30
31
  this.toggleContent();
31
32
  this.toggleButton?.classList.toggle('collapsed', this.collapsed);
32
33
  };
34
+ this.toggleButton.onclick = handleToggle;
35
+ this.toggleButton.addEventListener('keydown', handleEnterKey(handleToggle));
33
36
  this.contentDOM = document.createElement('div');
34
37
  this.contentDOM.classList.add('supplements-content');
35
38
  this.contentDOM.classList.add('block');
@@ -53,7 +53,7 @@ export interface EditorProps {
53
53
  location: Location;
54
54
  isComparingMode?: boolean;
55
55
  dispatch?: Dispatch;
56
- onEditorClick: (pos: number, node: ManuscriptNode, nodePos: number, event: MouseEvent) => void;
56
+ onEditorClick: (event: Event) => void;
57
57
  lockBody: boolean;
58
58
  isViewingMode?: boolean;
59
59
  hiddenNodeTypes?: ManuscriptNodeType[] | undefined;
@@ -26,6 +26,7 @@ export { CollabProvider } from './classes/collabProvider';
26
26
  export { PopperManager } from './lib/popper';
27
27
  export * from './toolbar';
28
28
  export * from './lib/capabilities';
29
+ export * from './lib/navigation-utils';
29
30
  export * from './lib/comments';
30
31
  export * from './lib/files';
31
32
  export * from './lib/footnotes';
@@ -20,6 +20,7 @@ export declare class ContextMenu {
20
20
  private readonly node;
21
21
  private readonly view;
22
22
  private readonly getPos;
23
+ private menuItems;
23
24
  constructor(node: ManuscriptNode, view: ManuscriptEditorView, getPos: () => number);
24
25
  showAddMenu: (target: Element) => void;
25
26
  showEditMenu: (target: Element) => void;
@@ -23,7 +23,7 @@ export declare enum MediaType {
23
23
  Figure = "figure",
24
24
  ExternalLink = "external_link"
25
25
  }
26
- export declare const createMediaPlaceholder: (mediaType?: MediaType, view?: ManuscriptEditorView, getPos?: () => number) => HTMLElement;
26
+ export declare const createMediaPlaceholder: (mediaType?: MediaType, view?: ManuscriptEditorView, getPos?: () => number, props?: EditorProps) => HTMLElement;
27
27
  export interface FileHandlers {
28
28
  handleDownload?: () => void;
29
29
  handleUpload?: () => void;
@@ -0,0 +1,45 @@
1
+ /*!
2
+ * © 2025 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { ManuscriptEditorState, ManuscriptEditorView } from '@manuscripts/transform';
17
+ import { EditorView } from 'prosemirror-view';
18
+ import { Dispatch } from '../commands';
19
+ export declare function focusNextElement(elements: Element[] | HTMLElement[], currentIndex: number, direction: 'forward' | 'backward'): void;
20
+ export declare function handleEnterKey(action: (event: KeyboardEvent) => void): (event: KeyboardEvent) => void;
21
+ export declare function handleArrowNavigation(event: KeyboardEvent, elements: HTMLElement[], currentElement: HTMLElement, keys: {
22
+ forward: 'ArrowDown' | 'ArrowRight';
23
+ backward: 'ArrowUp' | 'ArrowLeft';
24
+ }): void;
25
+ type ArrowKey = 'ArrowUp' | 'ArrowDown' | 'ArrowLeft' | 'ArrowRight';
26
+ type SupportedKey = ArrowKey | 'Enter' | 'Escape' | 'Tab';
27
+ type KeyHandlers = Partial<Record<SupportedKey, (event: KeyboardEvent) => void>>;
28
+ type NavigationConfig = {
29
+ getItems: () => HTMLElement[];
30
+ arrowKeys: {
31
+ forward: 'ArrowDown' | 'ArrowRight';
32
+ backward: 'ArrowUp' | 'ArrowLeft';
33
+ };
34
+ getCurrentElement?: (event: KeyboardEvent) => HTMLElement | null;
35
+ };
36
+ export type KeyboardInteractionOptions = {
37
+ container: HTMLElement | Window | Document;
38
+ navigation?: NavigationConfig;
39
+ additionalKeys?: KeyHandlers;
40
+ };
41
+ export declare function createKeyboardInteraction(options: KeyboardInteractionOptions): () => void;
42
+ export declare const focusNearestElement: (state: ManuscriptEditorState, dispatch?: Dispatch, view?: ManuscriptEditorView) => boolean;
43
+ export declare function getCursorContainer(view: EditorView): HTMLElement;
44
+ export declare function findNearestTabbable(container: HTMLElement, verticalPosition: number): HTMLElement | null;
45
+ export {};
@@ -17,8 +17,11 @@ import { Placement, StrictModifiers } from '@popperjs/core';
17
17
  export declare class PopperManager {
18
18
  private activePopper?;
19
19
  private handleDocumentClick?;
20
+ private triggerElement?;
21
+ private container?;
20
22
  show(target: Element, contents: HTMLElement, placement?: Placement, showArrow?: boolean, modifiers?: Array<Partial<StrictModifiers>>): void;
21
23
  destroy(): void;
24
+ getContainer(): HTMLElement | undefined;
22
25
  update(): void;
23
26
  isActive: () => boolean;
24
27
  private focusInput;
@@ -34,7 +34,7 @@ export declare const shouldRenderField: (field: string, type: BibliographyItemTy
34
34
  export declare const cleanItemValues: (item: BibliographyItemAttrs) => BibliographyItemAttrs;
35
35
  export declare const isBodyLocked: (state: EditorState) => boolean;
36
36
  export declare const isEditAllowed: (state: EditorState) => boolean;
37
- export declare const createToggleButton: (listener: () => void, what: string) => HTMLButtonElement;
37
+ export declare const createToggleButton: (listener: (event: MouseEvent | KeyboardEvent) => void, what: string) => HTMLButtonElement;
38
38
  export declare const getInsertPos: (type: ManuscriptNodeType, parent: ManuscriptNode, pos: number) => number;
39
39
  export declare const findInsertionPosition: (type: ManuscriptNodeType, doc: ManuscriptNode) => number;
40
40
  export declare const filterBlockNodes: (fragment: Fragment, predicate: (node: ProseMirrorNode) => boolean) => Fragment;
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "3.9.11";
1
+ export declare const VERSION = "3.9.13";
2
2
  export declare const MATHJAX_VERSION = "3.2.2";
@@ -2,9 +2,11 @@ import { LongDescNode } from '@manuscripts/transform';
2
2
  import BlockView from './block_view';
3
3
  export declare class AccessibilityElementView extends BlockView<LongDescNode> {
4
4
  contentDOM: HTMLElement;
5
+ private removeKeydownListener?;
5
6
  initialise(): void;
6
7
  createDOM(): void;
7
8
  createElement(): void;
9
+ destroy(): void;
8
10
  }
9
11
  declare const _default: (props: import("../configs/ManuscriptsEditor").EditorProps, dispatch?: import("..").Dispatch) => import("../types").NodeViewCreator<AccessibilityElementView>;
10
12
  export default _default;
@@ -17,8 +17,10 @@ import { AltTitleNode, ManuscriptNodeView } from '@manuscripts/transform';
17
17
  import { BaseNodeView } from './base_node_view';
18
18
  export declare class AltTitleView extends BaseNodeView<AltTitleNode> implements ManuscriptNodeView {
19
19
  contentDOM: HTMLElement;
20
+ private removeKeydownListener?;
20
21
  initialise: () => void;
21
22
  protected createDOM: () => void;
23
+ destroy(): void;
22
24
  }
23
25
  declare const _default: (props: import("../configs/ManuscriptsEditor").EditorProps, dispatch?: import("..").Dispatch) => import("../types").NodeViewCreator<AltTitleView>;
24
26
  export default _default;
@@ -26,7 +26,10 @@ export declare class BibliographyElementBlockView extends BlockView<Trackable<Bi
26
26
  ignoreMutation: () => boolean;
27
27
  private handleEdit;
28
28
  private showContextMenu;
29
+ private handleCommentMarkerInteraction;
30
+ private handleBibItemInteraction;
29
31
  private handleClick;
32
+ private handleKeyDown;
30
33
  updateContents(): void;
31
34
  createElement: () => void;
32
35
  private handleSave;
@@ -20,7 +20,7 @@ export declare class CitationEditableView extends CitationView {
20
20
  private can;
21
21
  createDOM(): void;
22
22
  stopEvent: (event: Event) => boolean;
23
- handleClick: (event: MouseEvent) => void;
23
+ handleClick: () => void;
24
24
  selectNode: () => void;
25
25
  destroy: () => void;
26
26
  showContextMenu: () => void;
@@ -26,10 +26,11 @@ export declare class ContributorsView extends BlockView<Trackable<ContributorsNo
26
26
  version: string;
27
27
  ignoreMutation: () => boolean;
28
28
  stopEvent: () => boolean;
29
+ private removeKeydownListener?;
29
30
  updateContents(): void;
30
31
  selectNode: () => void;
31
32
  buildAuthors: (affs: PluginState) => void;
32
- buildAuthor: (attrs: ContributorAttrs, isJointFirstAuthor: boolean) => HTMLSpanElement;
33
+ buildAuthor: (attrs: ContributorAttrs, isJointFirstAuthor: boolean, index: number) => HTMLSpanElement;
33
34
  private createNote;
34
35
  private isJointFirstAuthor;
35
36
  createElement: () => void;
@@ -45,6 +46,7 @@ export declare class ContributorsView extends BlockView<Trackable<ContributorsNo
45
46
  handleDeleteAuthor: (author: ContributorAttrs) => void;
46
47
  insertAuthorNode: (attrs: ContributorAttrs) => void;
47
48
  insertAffiliationNode: (attrs: AffiliationAttrs) => void;
49
+ destroy(): void;
48
50
  }
49
51
  declare const _default: (props: import("../configs/ManuscriptsEditor").EditorProps, dispatch?: import("..").Dispatch) => import("../types").NodeViewCreator<ContributorsView>;
50
52
  export default _default;