@manuscripts/body-editor 3.9.10 → 3.9.12
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.
- package/dist/cjs/commands.js +5 -2
- package/dist/cjs/components/keywords/AddKeywordInline.js +12 -1
- package/dist/cjs/components/views/FigureDropdown.js +65 -6
- package/dist/cjs/configs/ManuscriptsEditor.js +1 -1
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/keys/misc.js +2 -1
- package/dist/cjs/keys/title.js +0 -38
- package/dist/cjs/lib/comments.js +1 -0
- package/dist/cjs/lib/context-menu.js +74 -8
- package/dist/cjs/lib/media.js +27 -3
- package/dist/cjs/lib/navigation-utils.js +132 -0
- package/dist/cjs/lib/popper.js +25 -1
- package/dist/cjs/lib/position-menu.js +3 -0
- package/dist/cjs/lib/utils.js +7 -2
- package/dist/cjs/plugins/accessibility_element.js +10 -2
- package/dist/cjs/plugins/add-subtitle.js +8 -2
- package/dist/cjs/plugins/alt-titles.js +6 -1
- package/dist/cjs/plugins/comments.js +27 -15
- package/dist/cjs/plugins/persistent-cursor.js +4 -6
- package/dist/cjs/plugins/section_category.js +42 -9
- package/dist/cjs/plugins/translations.js +49 -13
- package/dist/cjs/versions.js +1 -1
- package/dist/cjs/views/accessibility_element.js +30 -0
- package/dist/cjs/views/alt_title.js +29 -0
- package/dist/cjs/views/alt_titles_section.js +9 -1
- package/dist/cjs/views/attachment.js +1 -1
- package/dist/cjs/views/bibliography_element.js +39 -17
- package/dist/cjs/views/citation.js +1 -0
- package/dist/cjs/views/citation_editable.js +4 -2
- package/dist/cjs/views/contributors.js +23 -2
- package/dist/cjs/views/cross_reference.js +3 -0
- package/dist/cjs/views/editable_block.js +37 -3
- package/dist/cjs/views/embed.js +3 -3
- package/dist/cjs/views/figure_editable.js +1 -1
- package/dist/cjs/views/figure_element.js +3 -0
- package/dist/cjs/views/footnote.js +3 -0
- package/dist/cjs/views/hero_image.js +4 -1
- package/dist/cjs/views/image_element.js +15 -7
- package/dist/cjs/views/inline_footnote.js +3 -0
- package/dist/cjs/views/keyword.js +15 -0
- package/dist/cjs/views/keyword_group.js +38 -0
- package/dist/cjs/views/quote_image_editable.js +1 -0
- package/dist/cjs/views/supplements.js +4 -1
- package/dist/es/commands.js +5 -2
- package/dist/es/components/keywords/AddKeywordInline.js +12 -1
- package/dist/es/components/views/FigureDropdown.js +66 -7
- package/dist/es/configs/ManuscriptsEditor.js +1 -1
- package/dist/es/index.js +1 -0
- package/dist/es/keys/misc.js +2 -1
- package/dist/es/keys/title.js +1 -39
- package/dist/es/lib/comments.js +1 -0
- package/dist/es/lib/context-menu.js +74 -8
- package/dist/es/lib/media.js +27 -3
- package/dist/es/lib/navigation-utils.js +122 -0
- package/dist/es/lib/popper.js +25 -1
- package/dist/es/lib/position-menu.js +3 -0
- package/dist/es/lib/utils.js +7 -2
- package/dist/es/plugins/accessibility_element.js +10 -2
- package/dist/es/plugins/add-subtitle.js +8 -2
- package/dist/es/plugins/alt-titles.js +6 -1
- package/dist/es/plugins/comments.js +27 -15
- package/dist/es/plugins/persistent-cursor.js +4 -6
- package/dist/es/plugins/section_category.js +42 -9
- package/dist/es/plugins/translations.js +49 -13
- package/dist/es/versions.js +1 -1
- package/dist/es/views/accessibility_element.js +30 -0
- package/dist/es/views/alt_title.js +29 -0
- package/dist/es/views/alt_titles_section.js +9 -1
- package/dist/es/views/attachment.js +1 -1
- package/dist/es/views/bibliography_element.js +39 -17
- package/dist/es/views/citation.js +1 -0
- package/dist/es/views/citation_editable.js +4 -2
- package/dist/es/views/contributors.js +23 -2
- package/dist/es/views/cross_reference.js +3 -0
- package/dist/es/views/editable_block.js +37 -3
- package/dist/es/views/embed.js +3 -3
- package/dist/es/views/figure_editable.js +1 -1
- package/dist/es/views/figure_element.js +3 -0
- package/dist/es/views/footnote.js +3 -0
- package/dist/es/views/hero_image.js +4 -1
- package/dist/es/views/image_element.js +15 -7
- package/dist/es/views/inline_footnote.js +3 -0
- package/dist/es/views/keyword.js +15 -0
- package/dist/es/views/keyword_group.js +38 -0
- package/dist/es/views/quote_image_editable.js +1 -0
- package/dist/es/views/supplements.js +4 -1
- package/dist/types/configs/ManuscriptsEditor.d.ts +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/lib/context-menu.d.ts +1 -0
- package/dist/types/lib/media.d.ts +1 -1
- package/dist/types/lib/navigation-utils.d.ts +45 -0
- package/dist/types/lib/popper.d.ts +3 -0
- package/dist/types/lib/utils.d.ts +1 -1
- package/dist/types/versions.d.ts +1 -1
- package/dist/types/views/accessibility_element.d.ts +2 -0
- package/dist/types/views/alt_title.d.ts +2 -0
- package/dist/types/views/bibliography_element.d.ts +3 -0
- package/dist/types/views/citation_editable.d.ts +1 -1
- package/dist/types/views/contributors.d.ts +3 -1
- package/dist/types/views/keyword.d.ts +1 -0
- package/dist/types/views/keyword_group.d.ts +4 -0
- package/package.json +4 -4
- package/styles/AdvancedEditor.css +116 -10
- package/styles/Editor.css +61 -6
- package/styles/popper.css +3 -1
package/dist/cjs/commands.js
CHANGED
|
@@ -38,6 +38,7 @@ const comments_2 = require("./plugins/comments");
|
|
|
38
38
|
const editor_props_1 = require("./plugins/editor-props");
|
|
39
39
|
const search_replace_1 = require("./plugins/search-replace");
|
|
40
40
|
const autocompletion_1 = require("./plugins/section_title/autocompletion");
|
|
41
|
+
const persistent_cursor_1 = require("./plugins/persistent-cursor");
|
|
41
42
|
const addToStart = (state, dispatch) => {
|
|
42
43
|
const { selection } = state;
|
|
43
44
|
const props = (0, editor_props_1.getEditorProps)(state);
|
|
@@ -1358,10 +1359,12 @@ const ignoreEnterInSubtitles = (state) => {
|
|
|
1358
1359
|
return false;
|
|
1359
1360
|
};
|
|
1360
1361
|
exports.ignoreEnterInSubtitles = ignoreEnterInSubtitles;
|
|
1361
|
-
const exitEditorToContainer = () => {
|
|
1362
|
+
const exitEditorToContainer = (state, dispatch, view) => {
|
|
1362
1363
|
const editorContainer = document.getElementById('editor');
|
|
1363
|
-
if (editorContainer) {
|
|
1364
|
+
if (editorContainer && dispatch && view) {
|
|
1364
1365
|
editorContainer.focus();
|
|
1366
|
+
const tr = view.state.tr.setMeta(persistent_cursor_1.persistentCursor, { on: true });
|
|
1367
|
+
view.dispatch(tr);
|
|
1365
1368
|
return true;
|
|
1366
1369
|
}
|
|
1367
1370
|
return false;
|
|
@@ -67,6 +67,12 @@ const NewKeywordButton = styled_components_1.default.button `
|
|
|
67
67
|
margin: 0;
|
|
68
68
|
padding: 0;
|
|
69
69
|
cursor: pointer;
|
|
70
|
+
|
|
71
|
+
&:focus-visible {
|
|
72
|
+
outline: 4px solid ${(props) => props.theme.colors.outline.focus};
|
|
73
|
+
outline-offset: 2px;
|
|
74
|
+
border-radius: 3px;
|
|
75
|
+
}
|
|
70
76
|
`;
|
|
71
77
|
const CreateKeywordButtonWrapper = styled_components_1.default.div `
|
|
72
78
|
position: absolute;
|
|
@@ -196,8 +202,13 @@ const AddKeywordInline = ({ viewProps, getUpdatedNode }) => {
|
|
|
196
202
|
},
|
|
197
203
|
};
|
|
198
204
|
return (react_1.default.createElement(AddNewKeyword, { ref: nodeRef },
|
|
199
|
-
!isAddingNewKeyword && (react_1.default.createElement(NewKeywordButton, { onClick: () => {
|
|
205
|
+
!isAddingNewKeyword && (react_1.default.createElement(NewKeywordButton, { tabIndex: -1, className: "keyword-add", onClick: () => {
|
|
200
206
|
setIsAddingNewKeyword(true);
|
|
207
|
+
}, onKeyDown: (e) => {
|
|
208
|
+
if (e.key === 'Enter') {
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
setIsAddingNewKeyword(true);
|
|
211
|
+
}
|
|
201
212
|
} }, "New keyword...")),
|
|
202
213
|
isAddingNewKeyword && react_1.default.createElement(KeywordInput, null),
|
|
203
214
|
isAddingNewKeyword && isValidNewKeyword() && (react_1.default.createElement(CreateKeywordButtonElement, null)),
|
|
@@ -42,6 +42,40 @@ const react_1 = __importStar(require("react"));
|
|
|
42
42
|
const styled_components_1 = __importDefault(require("styled-components"));
|
|
43
43
|
const files_1 = require("../../lib/files");
|
|
44
44
|
const get_media_type_1 = require("../../lib/get-media-type");
|
|
45
|
+
const navigation_utils_1 = require("../../lib/navigation-utils");
|
|
46
|
+
function useDropdownKeyboardNav(isOpen, containerRef, onEscape, onArrowLeft) {
|
|
47
|
+
(0, react_1.useEffect)(() => {
|
|
48
|
+
const container = containerRef.current;
|
|
49
|
+
if (!isOpen || !container) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const buttons = Array.from(container.querySelectorAll('button:not([disabled])'));
|
|
53
|
+
if (buttons.length === 0) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const removeKeydownListener = (0, navigation_utils_1.createKeyboardInteraction)({
|
|
57
|
+
container: container,
|
|
58
|
+
navigation: {
|
|
59
|
+
getItems: () => Array.from(container.querySelectorAll('button:not([disabled])')),
|
|
60
|
+
arrowKeys: {
|
|
61
|
+
forward: 'ArrowDown',
|
|
62
|
+
backward: 'ArrowUp',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
additionalKeys: {
|
|
66
|
+
Enter: (e) => e.target.click(),
|
|
67
|
+
Escape: () => onEscape(),
|
|
68
|
+
...(onArrowLeft ? { ArrowLeft: () => onArrowLeft() } : {}),
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
window.requestAnimationFrame(() => {
|
|
72
|
+
buttons[0]?.focus();
|
|
73
|
+
});
|
|
74
|
+
return () => {
|
|
75
|
+
removeKeydownListener();
|
|
76
|
+
};
|
|
77
|
+
}, [isOpen, containerRef, onEscape, onArrowLeft]);
|
|
78
|
+
}
|
|
45
79
|
function getSupplements(getFiles, getDoc, groupFiles, isEmbed) {
|
|
46
80
|
return groupFiles(getDoc(), getFiles())
|
|
47
81
|
.supplements.map((s) => s.file)
|
|
@@ -60,6 +94,7 @@ function getOtherFiles(getFiles, getDoc, groupFiles, isEmbed) {
|
|
|
60
94
|
}
|
|
61
95
|
const FigureOptions = ({ can, getDoc, getFiles, onDownload, onUpload, onDetach, onReplace, onReplaceEmbed, onDelete, isEmbed, hasSiblings, container, }) => {
|
|
62
96
|
const { isOpen, toggleOpen, wrapperRef } = (0, style_guide_1.useDropdown)();
|
|
97
|
+
const dropdownRef = (0, react_1.useRef)(null);
|
|
63
98
|
const showDownload = onDownload && can.downloadFiles;
|
|
64
99
|
const showUpload = onUpload && can.uploadFile;
|
|
65
100
|
const showDetach = onDetach && can.detachFile;
|
|
@@ -83,12 +118,18 @@ const FigureOptions = ({ can, getDoc, getFiles, onDownload, onUpload, onDetach,
|
|
|
83
118
|
container.classList.remove(activeClass);
|
|
84
119
|
}
|
|
85
120
|
}, [isOpen, container.classList]);
|
|
121
|
+
useDropdownKeyboardNav(isOpen, dropdownRef, toggleOpen);
|
|
86
122
|
const isEmbedMode = !!onReplaceEmbed;
|
|
87
123
|
const groupFiles = (0, files_1.memoGroupFiles)();
|
|
88
124
|
return (react_1.default.createElement(DropdownWrapper, { ref: wrapperRef },
|
|
89
|
-
react_1.default.createElement(OptionsButton, { className: 'options-button', onClick: toggleOpen
|
|
125
|
+
react_1.default.createElement(OptionsButton, { className: 'options-button', onClick: toggleOpen, onKeyDown: (e) => {
|
|
126
|
+
if (e.key === 'Enter') {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
toggleOpen();
|
|
129
|
+
}
|
|
130
|
+
} },
|
|
90
131
|
react_1.default.createElement(style_guide_1.DotsIcon, null)),
|
|
91
|
-
isOpen && (react_1.default.createElement(OptionsDropdownList, { direction: 'right', width: 128, top: 5 },
|
|
132
|
+
isOpen && (react_1.default.createElement(OptionsDropdownList, { direction: 'right', width: 128, top: 5, ref: dropdownRef },
|
|
92
133
|
showReplace && isEmbedMode && (react_1.default.createElement(ListItemButton, { onClick: () => onReplaceEmbed && onReplaceEmbed() }, "Edit Link")),
|
|
93
134
|
showReplace && !isEmbedMode && (react_1.default.createElement(NestedDropdown, { disabled: !showReplace, parentToggleOpen: toggleOpen, buttonText: replaceBtnText, moveLeft: true, list: react_1.default.createElement(react_1.default.Fragment, null,
|
|
94
135
|
getSupplements(getFiles, getDoc, groupFiles, isEmbed).map((file, index) => (react_1.default.createElement(ListItemButton, { key: file.id, id: index.toString(), onClick: () => onReplace && onReplace(file, true) },
|
|
@@ -107,11 +148,23 @@ const FigureOptions = ({ can, getDoc, getFiles, onDownload, onUpload, onDetach,
|
|
|
107
148
|
exports.FigureOptions = FigureOptions;
|
|
108
149
|
const NestedDropdown = ({ parentToggleOpen, buttonText, disabled, list, moveLeft }) => {
|
|
109
150
|
const { isOpen, toggleOpen, wrapperRef } = (0, style_guide_1.useDropdown)();
|
|
151
|
+
const nestedListRef = (0, react_1.useRef)(null);
|
|
152
|
+
const handleArrowLeft = (0, react_1.useCallback)(() => {
|
|
153
|
+
toggleOpen();
|
|
154
|
+
const parentButton = wrapperRef.current?.querySelector('.nested-list-button');
|
|
155
|
+
parentButton?.focus();
|
|
156
|
+
}, [toggleOpen, wrapperRef]);
|
|
157
|
+
useDropdownKeyboardNav(isOpen, nestedListRef, handleArrowLeft, handleArrowLeft);
|
|
110
158
|
return (react_1.default.createElement(DropdownWrapper, { ref: wrapperRef },
|
|
111
|
-
react_1.default.createElement(NestedListButton, { onClick: toggleOpen, disabled: disabled
|
|
159
|
+
react_1.default.createElement(NestedListButton, { className: "nested-list-button", onClick: toggleOpen, disabled: disabled, onKeyDown: (e) => {
|
|
160
|
+
if (e.key === 'ArrowRight') {
|
|
161
|
+
e.preventDefault();
|
|
162
|
+
toggleOpen();
|
|
163
|
+
}
|
|
164
|
+
} },
|
|
112
165
|
react_1.default.createElement("div", null, buttonText),
|
|
113
166
|
react_1.default.createElement(style_guide_1.TriangleCollapsedIcon, null)),
|
|
114
|
-
isOpen && (react_1.default.createElement(NestedListDropdownList, { direction: 'right', moveLeft: moveLeft, width: 192, onClick: (e) => {
|
|
167
|
+
isOpen && (react_1.default.createElement(NestedListDropdownList, { direction: 'right', moveLeft: moveLeft, width: 192, ref: nestedListRef, onClick: (e) => {
|
|
115
168
|
toggleOpen();
|
|
116
169
|
parentToggleOpen(e);
|
|
117
170
|
} }, list))));
|
|
@@ -154,7 +207,8 @@ const ListItemButton = (0, styled_components_1.default)(style_guide_1.IconTextBu
|
|
|
154
207
|
justify-content: space-between;
|
|
155
208
|
align-items: center;
|
|
156
209
|
|
|
157
|
-
:hover
|
|
210
|
+
:hover,
|
|
211
|
+
:focus-visible {
|
|
158
212
|
background: #f2fbfc;
|
|
159
213
|
}
|
|
160
214
|
|
|
@@ -173,7 +227,8 @@ const ListItemText = styled_components_1.default.div `
|
|
|
173
227
|
const NestedListButton = (0, styled_components_1.default)(ListItemButton) `
|
|
174
228
|
width: 100%;
|
|
175
229
|
&:active,
|
|
176
|
-
&:focus
|
|
230
|
+
&:focus,
|
|
231
|
+
&:focus-visible {
|
|
177
232
|
background: #f2fbfc;
|
|
178
233
|
}
|
|
179
234
|
svg {
|
|
@@ -188,4 +243,8 @@ const UploadButton = (0, styled_components_1.default)(style_guide_1.IconTextButt
|
|
|
188
243
|
border-top: 1px solid #f2f2f2;
|
|
189
244
|
padding: ${(props) => props.theme.grid.unit * 4}px;
|
|
190
245
|
justify-content: flex-start;
|
|
246
|
+
|
|
247
|
+
&:focus-visible {
|
|
248
|
+
background: #f2fbfc;
|
|
249
|
+
}
|
|
191
250
|
`;
|
|
@@ -56,7 +56,7 @@ const createEditorView = (props, root, state, dispatch) => {
|
|
|
56
56
|
handleScrollToSelection: helpers_1.handleScrollToSelectedTarget,
|
|
57
57
|
transformCopied: copy_1.transformCopied,
|
|
58
58
|
handleClickOn: (view, pos, node, nodePos, event) => {
|
|
59
|
-
props.onEditorClick(
|
|
59
|
+
props.onEditorClick(event);
|
|
60
60
|
if (event?.target &&
|
|
61
61
|
event.target.classList.contains('table-context-menu-button')) {
|
|
62
62
|
return true;
|
package/dist/cjs/index.js
CHANGED
|
@@ -51,6 +51,7 @@ var popper_1 = require("./lib/popper");
|
|
|
51
51
|
Object.defineProperty(exports, "PopperManager", { enumerable: true, get: function () { return popper_1.PopperManager; } });
|
|
52
52
|
__exportStar(require("./toolbar"), exports);
|
|
53
53
|
__exportStar(require("./lib/capabilities"), exports);
|
|
54
|
+
__exportStar(require("./lib/navigation-utils"), exports);
|
|
54
55
|
__exportStar(require("./lib/comments"), exports);
|
|
55
56
|
__exportStar(require("./lib/files"), exports);
|
|
56
57
|
__exportStar(require("./lib/footnotes"), exports);
|
package/dist/cjs/keys/misc.js
CHANGED
|
@@ -22,10 +22,11 @@ const prosemirror_inputrules_1 = require("prosemirror-inputrules");
|
|
|
22
22
|
const prosemirror_tables_1 = require("prosemirror-tables");
|
|
23
23
|
const commands_1 = require("../commands");
|
|
24
24
|
const list_1 = require("./list");
|
|
25
|
+
const navigation_utils_1 = require("../lib/navigation-utils");
|
|
25
26
|
const customKeymap = {
|
|
26
27
|
Backspace: (0, prosemirror_commands_1.chainCommands)(prosemirror_inputrules_1.undoInputRule, commands_1.ignoreAtomBlockNodeBackward, commands_1.ignoreMetaNodeBackspaceCommand, (0, list_1.skipCommandTracking)(prosemirror_commands_1.joinBackward)),
|
|
27
28
|
Delete: commands_1.ignoreAtomBlockNodeForward,
|
|
28
|
-
Tab: (0, prosemirror_tables_1.goToNextCell)(1),
|
|
29
|
+
Tab: (0, prosemirror_commands_1.chainCommands)((0, prosemirror_tables_1.goToNextCell)(1), navigation_utils_1.focusNearestElement),
|
|
29
30
|
Escape: commands_1.exitEditorToContainer,
|
|
30
31
|
'Mod-z': prosemirror_history_1.undo,
|
|
31
32
|
'Mod-y': prosemirror_history_1.redo,
|
package/dist/cjs/keys/title.js
CHANGED
|
@@ -34,42 +34,6 @@ const insertParagraph = (dispatch, state, $anchor) => {
|
|
|
34
34
|
tr.setSelection(prosemirror_state_1.TextSelection.create(tr.doc, pos + 1)).scrollIntoView();
|
|
35
35
|
dispatch(tr);
|
|
36
36
|
};
|
|
37
|
-
const enterNextBlock = (dispatch, state, $anchor, create) => {
|
|
38
|
-
const { schema: { nodes }, tr, } = state;
|
|
39
|
-
const pos = $anchor.after($anchor.depth - 1);
|
|
40
|
-
let selection = prosemirror_state_1.Selection.findFrom(tr.doc.resolve(pos), 1, true);
|
|
41
|
-
if (!selection && create) {
|
|
42
|
-
tr.insert(pos, nodes.paragraph.create());
|
|
43
|
-
selection = prosemirror_state_1.Selection.findFrom(tr.doc.resolve(pos), 1, true);
|
|
44
|
-
}
|
|
45
|
-
if (!selection) {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
tr.setSelection(selection).scrollIntoView();
|
|
49
|
-
dispatch(tr);
|
|
50
|
-
return true;
|
|
51
|
-
};
|
|
52
|
-
const enterPreviousBlock = (dispatch, state, $anchor) => {
|
|
53
|
-
const { tr } = state;
|
|
54
|
-
const offset = $anchor.nodeBefore ? $anchor.nodeBefore.nodeSize : 0;
|
|
55
|
-
const $pos = tr.doc.resolve($anchor.pos - offset - 1);
|
|
56
|
-
const previous = prosemirror_state_1.Selection.findFrom($pos, -1, true);
|
|
57
|
-
if (!previous) {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
tr.setSelection(prosemirror_state_1.TextSelection.create(tr.doc, previous.from)).scrollIntoView();
|
|
61
|
-
dispatch(tr);
|
|
62
|
-
return true;
|
|
63
|
-
};
|
|
64
|
-
const exitBlock = (direction) => (state, dispatch) => {
|
|
65
|
-
const { selection: { $anchor }, } = state;
|
|
66
|
-
if (dispatch) {
|
|
67
|
-
return direction === 1
|
|
68
|
-
? enterNextBlock(dispatch, state, $anchor)
|
|
69
|
-
: enterPreviousBlock(dispatch, state, $anchor);
|
|
70
|
-
}
|
|
71
|
-
return true;
|
|
72
|
-
};
|
|
73
37
|
const leaveTitle = (state, dispatch, view) => {
|
|
74
38
|
const { selection } = state;
|
|
75
39
|
if (!(0, commands_1.isTextSelection)(selection)) {
|
|
@@ -154,8 +118,6 @@ const keepCaption = (state) => {
|
|
|
154
118
|
const titleKeymap = {
|
|
155
119
|
Backspace: (0, prosemirror_commands_1.chainCommands)(protectTitles, exports.protectReferencesTitle, protectCaption),
|
|
156
120
|
Enter: (0, prosemirror_commands_1.chainCommands)(commands_1.autoComplete, leaveTitle, leaveFigcaption),
|
|
157
|
-
Tab: exitBlock(1),
|
|
158
121
|
Delete: (0, prosemirror_commands_1.chainCommands)(keepCaption, exports.protectReferencesTitle),
|
|
159
|
-
'Shift-Tab': exitBlock(-1),
|
|
160
122
|
};
|
|
161
123
|
exports.default = titleKeymap;
|
package/dist/cjs/lib/comments.js
CHANGED
|
@@ -35,6 +35,7 @@ const createCommentMarker = (tagName, key, count) => {
|
|
|
35
35
|
element.id = getMarkerID(key);
|
|
36
36
|
element.dataset.key = key;
|
|
37
37
|
element.classList.add('comment-marker');
|
|
38
|
+
element.tabIndex = 0;
|
|
38
39
|
if (count && count > 1) {
|
|
39
40
|
element.dataset.count = String(count);
|
|
40
41
|
}
|
|
@@ -19,6 +19,7 @@ exports.ContextMenu = exports.contextMenuBtnClass = exports.sectionLevel = void
|
|
|
19
19
|
const style_guide_1 = require("@manuscripts/style-guide");
|
|
20
20
|
const transform_1 = require("@manuscripts/transform");
|
|
21
21
|
const prosemirror_state_1 = require("prosemirror-state");
|
|
22
|
+
const navigation_utils_1 = require("./navigation-utils");
|
|
22
23
|
const prosemirror_utils_1 = require("prosemirror-utils");
|
|
23
24
|
const react_1 = require("react");
|
|
24
25
|
const server_1 = require("react-dom/server");
|
|
@@ -52,7 +53,9 @@ exports.contextMenuBtnClass = 'btn-context-menu';
|
|
|
52
53
|
const contextSubmenuBtnClass = 'context-submenu-trigger';
|
|
53
54
|
class ContextMenu {
|
|
54
55
|
constructor(node, view, getPos) {
|
|
56
|
+
this.menuItems = [];
|
|
55
57
|
this.showAddMenu = (target) => {
|
|
58
|
+
this.menuItems = [];
|
|
56
59
|
const menu = document.createElement('div');
|
|
57
60
|
menu.className = 'menu';
|
|
58
61
|
const $pos = this.resolvePos();
|
|
@@ -150,6 +153,7 @@ class ContextMenu {
|
|
|
150
153
|
this.addPopperEventListeners();
|
|
151
154
|
};
|
|
152
155
|
this.showEditMenu = (target) => {
|
|
156
|
+
this.menuItems = [];
|
|
153
157
|
const menu = document.createElement('div');
|
|
154
158
|
menu.className = 'menu';
|
|
155
159
|
const $pos = this.resolvePos();
|
|
@@ -160,7 +164,7 @@ class ContextMenu {
|
|
|
160
164
|
const figure = (0, utils_1.getMatchingChild)(this.node, (node) => node.type === transform_1.schema.nodes.figure);
|
|
161
165
|
if (figure) {
|
|
162
166
|
const attrType = figure.attrs.type;
|
|
163
|
-
const submenuOptions = (0, position_menu_1.createPositionOptions)(transform_1.schema.nodes.figure, figure, attrType, this.view);
|
|
167
|
+
const submenuOptions = (0, position_menu_1.createPositionOptions)(transform_1.schema.nodes.figure, figure, attrType, this.view, () => popper.destroy());
|
|
164
168
|
const submenuLabel = 'Position';
|
|
165
169
|
const submenu = this.createSubmenu(submenuLabel, submenuOptions);
|
|
166
170
|
menu.appendChild(submenu);
|
|
@@ -278,16 +282,28 @@ class ContextMenu {
|
|
|
278
282
|
this.createSubmenuTrigger = (contents) => {
|
|
279
283
|
const item = document.createElement('div');
|
|
280
284
|
item.className = 'menu-item';
|
|
285
|
+
item.tabIndex = 0;
|
|
281
286
|
const textNode = document.createTextNode(contents);
|
|
282
287
|
item.innerHTML = (0, server_1.renderToStaticMarkup)((0, react_1.createElement)(style_guide_1.TriangleCollapsedIcon));
|
|
283
288
|
item.prepend(textNode);
|
|
284
289
|
item.classList.add(contextSubmenuBtnClass);
|
|
285
290
|
item.addEventListener('mousedown', this.toggleSubmenu);
|
|
291
|
+
item.addEventListener('keydown', (0, navigation_utils_1.handleEnterKey)((e) => {
|
|
292
|
+
const target = e.target;
|
|
293
|
+
this.toggleSubmenu(e);
|
|
294
|
+
const submenuContent = target.nextElementSibling;
|
|
295
|
+
if (submenuContent?.classList.contains('show')) {
|
|
296
|
+
const firstItem = submenuContent.querySelector('.menu-item');
|
|
297
|
+
firstItem?.focus();
|
|
298
|
+
}
|
|
299
|
+
}));
|
|
300
|
+
this.menuItems.push(item);
|
|
286
301
|
return item;
|
|
287
302
|
};
|
|
288
303
|
this.createMenuItem = (contents, handler, IconComponent = null, selected = false) => {
|
|
289
304
|
const item = document.createElement('div');
|
|
290
305
|
item.className = 'menu-item';
|
|
306
|
+
item.setAttribute('tabindex', '0');
|
|
291
307
|
selected && item.classList.add('selected');
|
|
292
308
|
if (IconComponent) {
|
|
293
309
|
if (typeof IconComponent === 'string') {
|
|
@@ -303,6 +319,8 @@ class ContextMenu {
|
|
|
303
319
|
event.preventDefault();
|
|
304
320
|
handler(event);
|
|
305
321
|
});
|
|
322
|
+
item.addEventListener('keydown', (0, navigation_utils_1.handleEnterKey)(handler));
|
|
323
|
+
this.menuItems.push(item);
|
|
306
324
|
return item;
|
|
307
325
|
};
|
|
308
326
|
this.createMenuSection = (createMenuItems, isSubmenu = false) => {
|
|
@@ -399,14 +417,62 @@ class ContextMenu {
|
|
|
399
417
|
popper.destroy();
|
|
400
418
|
});
|
|
401
419
|
};
|
|
402
|
-
const keyListener = (event) => {
|
|
403
|
-
if (event.key === 'Escape') {
|
|
404
|
-
window.removeEventListener('keydown', keyListener);
|
|
405
|
-
popper.destroy();
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
420
|
window.addEventListener('mousedown', mouseListener);
|
|
409
|
-
window.
|
|
421
|
+
window.requestAnimationFrame(() => {
|
|
422
|
+
const popperContainer = popper.getContainer();
|
|
423
|
+
if (popperContainer) {
|
|
424
|
+
(0, navigation_utils_1.createKeyboardInteraction)({
|
|
425
|
+
container: popperContainer,
|
|
426
|
+
navigation: {
|
|
427
|
+
getItems: () => {
|
|
428
|
+
const activeElement = document.activeElement;
|
|
429
|
+
const openSubmenu = activeElement?.closest('.menu-section.menu.show');
|
|
430
|
+
if (openSubmenu) {
|
|
431
|
+
return this.menuItems.filter((item) => openSubmenu.contains(item));
|
|
432
|
+
}
|
|
433
|
+
return this.menuItems.filter((item) => {
|
|
434
|
+
const menuSection = item.closest('.menu-section');
|
|
435
|
+
if (menuSection && menuSection.classList.contains('menu')) {
|
|
436
|
+
return menuSection.classList.contains('show');
|
|
437
|
+
}
|
|
438
|
+
return true;
|
|
439
|
+
});
|
|
440
|
+
},
|
|
441
|
+
arrowKeys: {
|
|
442
|
+
forward: 'ArrowDown',
|
|
443
|
+
backward: 'ArrowUp',
|
|
444
|
+
},
|
|
445
|
+
getCurrentElement: () => document.activeElement,
|
|
446
|
+
},
|
|
447
|
+
additionalKeys: {
|
|
448
|
+
ArrowRight: (event) => {
|
|
449
|
+
const target = event.target;
|
|
450
|
+
if (target.classList.contains(contextSubmenuBtnClass)) {
|
|
451
|
+
this.toggleSubmenu(event);
|
|
452
|
+
const submenuContent = target.nextElementSibling;
|
|
453
|
+
if (submenuContent?.classList.contains('show')) {
|
|
454
|
+
const firstItem = submenuContent.querySelector('.menu-item');
|
|
455
|
+
firstItem?.focus();
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
ArrowLeft: (event) => {
|
|
460
|
+
const target = event.target;
|
|
461
|
+
const submenu = target.closest('.context-submenu');
|
|
462
|
+
if (submenu) {
|
|
463
|
+
const trigger = submenu.querySelector(`.${contextSubmenuBtnClass}`);
|
|
464
|
+
if (trigger) {
|
|
465
|
+
const submenuContent = trigger.nextElementSibling;
|
|
466
|
+
submenuContent?.classList.toggle('show');
|
|
467
|
+
trigger.focus();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
this.menuItems[0]?.focus();
|
|
475
|
+
});
|
|
410
476
|
};
|
|
411
477
|
this.trimTitle = (title, max) => {
|
|
412
478
|
return title.length > max ? title.substring(0, max) + '…' : title;
|
package/dist/cjs/lib/media.js
CHANGED
|
@@ -21,6 +21,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
21
21
|
exports.addInteractionHandlers = exports.createFileUploader = exports.createReactTools = exports.createFileHandlers = exports.createMediaPlaceholder = exports.MediaType = exports.createUnsupportedFormat = void 0;
|
|
22
22
|
const transform_1 = require("@manuscripts/transform");
|
|
23
23
|
const InsertEmbedDialog_1 = require("../components/toolbar/InsertEmbedDialog");
|
|
24
|
+
const navigation_utils_1 = require("./navigation-utils");
|
|
24
25
|
const FigureDropdown_1 = require("../components/views/FigureDropdown");
|
|
25
26
|
const icons_1 = require("../icons");
|
|
26
27
|
const ReactSubView_1 = __importDefault(require("../views/ReactSubView"));
|
|
@@ -57,9 +58,10 @@ const MediaLabels = {
|
|
|
57
58
|
[MediaType.Figure]: 'figure',
|
|
58
59
|
[MediaType.ExternalLink]: 'a file to link',
|
|
59
60
|
};
|
|
60
|
-
const createMediaPlaceholder = (mediaType = MediaType.Media, view, getPos) => {
|
|
61
|
+
const createMediaPlaceholder = (mediaType = MediaType.Media, view, getPos, props) => {
|
|
61
62
|
const element = document.createElement('div');
|
|
62
63
|
element.classList.add('figure', 'placeholder');
|
|
64
|
+
element.tabIndex = 0;
|
|
63
65
|
const instructions = document.createElement('div');
|
|
64
66
|
instructions.classList.add('instructions');
|
|
65
67
|
instructions.innerHTML = `
|
|
@@ -79,14 +81,29 @@ const createMediaPlaceholder = (mediaType = MediaType.Media, view, getPos) => {
|
|
|
79
81
|
`;
|
|
80
82
|
if (mediaType === MediaType.Media && view && getPos) {
|
|
81
83
|
const embedLink = instructions.querySelector("[data-action='add-external-link']");
|
|
84
|
+
embedLink.tabIndex = 0;
|
|
82
85
|
if (embedLink) {
|
|
83
86
|
embedLink.addEventListener('click', (e) => {
|
|
84
87
|
e.stopPropagation();
|
|
85
88
|
e.preventDefault();
|
|
86
89
|
(0, InsertEmbedDialog_1.openEmbedDialog)(view, getPos());
|
|
87
90
|
});
|
|
91
|
+
embedLink.addEventListener('keydown', (0, navigation_utils_1.handleEnterKey)((e) => {
|
|
92
|
+
e.stopPropagation();
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
(0, InsertEmbedDialog_1.openEmbedDialog)(view, getPos());
|
|
95
|
+
}));
|
|
88
96
|
}
|
|
89
97
|
}
|
|
98
|
+
const links = instructions.querySelectorAll(`[data-action='open-other-files'], [data-action='open-supplement-files'] `);
|
|
99
|
+
links.forEach((link) => {
|
|
100
|
+
link.tabIndex = 0;
|
|
101
|
+
link.addEventListener('keydown', (0, navigation_utils_1.handleEnterKey)((event) => {
|
|
102
|
+
event.preventDefault();
|
|
103
|
+
event.stopPropagation();
|
|
104
|
+
props?.onEditorClick(event);
|
|
105
|
+
}));
|
|
106
|
+
});
|
|
90
107
|
element.appendChild(instructions);
|
|
91
108
|
return element;
|
|
92
109
|
};
|
|
@@ -165,8 +182,7 @@ const createFileUploader = (handler, accept = '*/*') => {
|
|
|
165
182
|
};
|
|
166
183
|
exports.createFileUploader = createFileUploader;
|
|
167
184
|
const addInteractionHandlers = (element, uploadFn, accept = '*/*') => {
|
|
168
|
-
const
|
|
169
|
-
const target = event.target;
|
|
185
|
+
const handlePlaceholderInteraction = (target) => {
|
|
170
186
|
if (target.dataset && target.dataset.action) {
|
|
171
187
|
return;
|
|
172
188
|
}
|
|
@@ -181,7 +197,15 @@ const addInteractionHandlers = (element, uploadFn, accept = '*/*') => {
|
|
|
181
197
|
});
|
|
182
198
|
input.click();
|
|
183
199
|
};
|
|
200
|
+
const handlePlaceholderClick = (event) => {
|
|
201
|
+
const target = event.target;
|
|
202
|
+
handlePlaceholderInteraction(target);
|
|
203
|
+
};
|
|
184
204
|
element.addEventListener('click', handlePlaceholderClick);
|
|
205
|
+
element.addEventListener('keydown', (0, navigation_utils_1.handleEnterKey)(() => {
|
|
206
|
+
const target = document.activeElement;
|
|
207
|
+
handlePlaceholderInteraction(target);
|
|
208
|
+
}));
|
|
185
209
|
element.addEventListener('mouseenter', () => {
|
|
186
210
|
element.classList.toggle('over', true);
|
|
187
211
|
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*!
|
|
3
|
+
* © 2025 Atypon Systems LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.focusNearestElement = void 0;
|
|
19
|
+
exports.focusNextElement = focusNextElement;
|
|
20
|
+
exports.handleEnterKey = handleEnterKey;
|
|
21
|
+
exports.handleArrowNavigation = handleArrowNavigation;
|
|
22
|
+
exports.createKeyboardInteraction = createKeyboardInteraction;
|
|
23
|
+
exports.getCursorContainer = getCursorContainer;
|
|
24
|
+
exports.findNearestTabbable = findNearestTabbable;
|
|
25
|
+
const utils_1 = require("./utils");
|
|
26
|
+
function focusNextElement(elements, currentIndex, direction) {
|
|
27
|
+
const nextIndex = direction === 'forward'
|
|
28
|
+
? (currentIndex + 1) % elements.length
|
|
29
|
+
: (currentIndex - 1 + elements.length) % elements.length;
|
|
30
|
+
const nextElement = elements[nextIndex];
|
|
31
|
+
nextElement?.focus();
|
|
32
|
+
}
|
|
33
|
+
function handleEnterKey(action) {
|
|
34
|
+
return (event) => {
|
|
35
|
+
if (event.key === 'Enter') {
|
|
36
|
+
event.preventDefault();
|
|
37
|
+
action(event);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function handleArrowNavigation(event, elements, currentElement, keys) {
|
|
42
|
+
const { forward, backward } = keys;
|
|
43
|
+
if (event.key !== forward && event.key !== backward) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
if (elements.length === 0) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const currentIndex = elements.indexOf(currentElement);
|
|
51
|
+
if (currentIndex === -1) {
|
|
52
|
+
elements[0]?.focus();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const direction = event.key === forward ? 'forward' : 'backward';
|
|
56
|
+
focusNextElement(elements, currentIndex, direction);
|
|
57
|
+
}
|
|
58
|
+
function createKeyboardInteraction(options) {
|
|
59
|
+
const { container, additionalKeys, navigation } = options;
|
|
60
|
+
const handleKeydown = (event) => {
|
|
61
|
+
const e = event;
|
|
62
|
+
const key = e.key;
|
|
63
|
+
const handler = additionalKeys?.[key];
|
|
64
|
+
if (handler) {
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
handler(e);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (!navigation) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const { getItems, arrowKeys, getCurrentElement } = navigation;
|
|
73
|
+
const currentElement = getCurrentElement
|
|
74
|
+
? getCurrentElement(e)
|
|
75
|
+
: e.target;
|
|
76
|
+
if (!currentElement) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const list = getItems();
|
|
80
|
+
if (!list.length) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
handleArrowNavigation(e, list, currentElement, arrowKeys);
|
|
84
|
+
};
|
|
85
|
+
container.addEventListener('keydown', handleKeydown);
|
|
86
|
+
return () => {
|
|
87
|
+
container.removeEventListener('keydown', handleKeydown);
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const focusNearestElement = (state, dispatch, view) => {
|
|
91
|
+
if (!view) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const active = document.activeElement;
|
|
95
|
+
if (!active || !active.classList.contains('manuscript-editor')) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const { from } = view.state.selection;
|
|
99
|
+
const coords = view.coordsAtPos(from);
|
|
100
|
+
const container = getCursorContainer(view);
|
|
101
|
+
const target = findNearestTabbable(container, coords.top);
|
|
102
|
+
if (!target) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
target.focus();
|
|
106
|
+
return true;
|
|
107
|
+
};
|
|
108
|
+
exports.focusNearestElement = focusNearestElement;
|
|
109
|
+
function getCursorContainer(view) {
|
|
110
|
+
const scoped = (0, utils_1.findParentNodeWithIdValue)(view.state.selection);
|
|
111
|
+
if (scoped) {
|
|
112
|
+
const dom = view.nodeDOM(scoped.pos);
|
|
113
|
+
if (dom instanceof HTMLElement) {
|
|
114
|
+
return dom;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return view.dom;
|
|
118
|
+
}
|
|
119
|
+
function findNearestTabbable(container, verticalPosition) {
|
|
120
|
+
const tabbables = container.querySelectorAll('a[href], button, [tabindex]:not([tabindex="-1"])');
|
|
121
|
+
let target = null;
|
|
122
|
+
let minDistance = null;
|
|
123
|
+
tabbables.forEach((el) => {
|
|
124
|
+
const rect = el.getBoundingClientRect();
|
|
125
|
+
const distance = Math.abs(rect.top - verticalPosition);
|
|
126
|
+
if (minDistance === null || distance < minDistance) {
|
|
127
|
+
minDistance = distance;
|
|
128
|
+
target = el;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
return target;
|
|
132
|
+
}
|