@atlaskit/editor-common 74.29.3 → 74.31.0
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/CHANGELOG.md +12 -0
- package/dist/cjs/i18n/cs.js +19 -0
- package/dist/cjs/i18n/da.js +19 -0
- package/dist/cjs/i18n/de.js +19 -0
- package/dist/cjs/i18n/es.js +19 -0
- package/dist/cjs/i18n/fi.js +19 -0
- package/dist/cjs/i18n/fr.js +19 -0
- package/dist/cjs/i18n/hu.js +19 -0
- package/dist/cjs/i18n/it.js +19 -0
- package/dist/cjs/i18n/ja.js +19 -0
- package/dist/cjs/i18n/ko.js +19 -0
- package/dist/cjs/i18n/nb.js +19 -0
- package/dist/cjs/i18n/nl.js +19 -0
- package/dist/cjs/i18n/pl.js +19 -0
- package/dist/cjs/i18n/pt_BR.js +19 -0
- package/dist/cjs/i18n/ru.js +19 -0
- package/dist/cjs/i18n/sv.js +19 -0
- package/dist/cjs/i18n/th.js +19 -0
- package/dist/cjs/i18n/tr.js +19 -0
- package/dist/cjs/i18n/uk.js +19 -0
- package/dist/cjs/i18n/vi.js +19 -0
- package/dist/cjs/i18n/zh.js +19 -0
- package/dist/cjs/i18n/zh_TW.js +19 -0
- package/dist/cjs/lists/analytics.js +40 -0
- package/dist/cjs/lists/indentation.js +24 -0
- package/dist/cjs/lists/index.js +89 -0
- package/dist/cjs/lists/node.js +97 -0
- package/dist/cjs/lists/replace-content.js +24 -0
- package/dist/cjs/lists/selection.js +59 -0
- package/dist/cjs/mark/commands.js +201 -0
- package/dist/cjs/mark/index.js +30 -0
- package/dist/cjs/messages/full-page.js +25 -0
- package/dist/cjs/messages/index.js +7 -0
- package/dist/cjs/monitoring/error.js +1 -1
- package/dist/cjs/node-width/index.js +10 -2
- package/dist/cjs/ui/DropList/index.js +1 -1
- package/dist/cjs/ui/Toolbar/index.js +8 -0
- package/dist/cjs/ui/index.js +7 -0
- package/dist/cjs/ui-menu/DropdownMenu/index.js +16 -2
- package/dist/cjs/ui-menu/ToolbarArrowKeyNavigationProvider/index.js +219 -0
- package/dist/cjs/ui-menu/index.js +23 -4
- package/dist/cjs/utils/commands.js +110 -2
- package/dist/cjs/utils/document.js +26 -1
- package/dist/cjs/utils/index.js +51 -2
- package/dist/cjs/utils/prosemirror/autojoin.js +68 -0
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/i18n/cs.js +19 -0
- package/dist/es2019/i18n/da.js +19 -0
- package/dist/es2019/i18n/de.js +19 -0
- package/dist/es2019/i18n/es.js +19 -0
- package/dist/es2019/i18n/fi.js +19 -0
- package/dist/es2019/i18n/fr.js +19 -0
- package/dist/es2019/i18n/hu.js +19 -0
- package/dist/es2019/i18n/it.js +19 -0
- package/dist/es2019/i18n/ja.js +19 -0
- package/dist/es2019/i18n/ko.js +19 -0
- package/dist/es2019/i18n/nb.js +19 -0
- package/dist/es2019/i18n/nl.js +19 -0
- package/dist/es2019/i18n/pl.js +19 -0
- package/dist/es2019/i18n/pt_BR.js +19 -0
- package/dist/es2019/i18n/ru.js +19 -0
- package/dist/es2019/i18n/sv.js +19 -0
- package/dist/es2019/i18n/th.js +19 -0
- package/dist/es2019/i18n/tr.js +19 -0
- package/dist/es2019/i18n/uk.js +19 -0
- package/dist/es2019/i18n/vi.js +19 -0
- package/dist/es2019/i18n/zh.js +19 -0
- package/dist/es2019/i18n/zh_TW.js +19 -0
- package/dist/es2019/lists/analytics.js +36 -0
- package/dist/es2019/lists/indentation.js +18 -0
- package/dist/es2019/lists/index.js +6 -0
- package/dist/es2019/lists/node.js +97 -0
- package/dist/es2019/lists/replace-content.js +18 -0
- package/dist/es2019/lists/selection.js +53 -0
- package/dist/es2019/mark/commands.js +205 -0
- package/dist/es2019/mark/index.js +1 -0
- package/dist/es2019/messages/full-page.js +18 -0
- package/dist/es2019/messages/index.js +1 -0
- package/dist/es2019/monitoring/error.js +1 -1
- package/dist/es2019/node-width/index.js +7 -0
- package/dist/es2019/ui/DropList/index.js +1 -1
- package/dist/es2019/ui/Toolbar/index.js +1 -0
- package/dist/es2019/ui/index.js +2 -1
- package/dist/es2019/ui-menu/DropdownMenu/index.js +16 -2
- package/dist/es2019/ui-menu/ToolbarArrowKeyNavigationProvider/index.js +209 -0
- package/dist/es2019/ui-menu/index.js +5 -4
- package/dist/es2019/utils/commands.js +106 -2
- package/dist/es2019/utils/document.js +25 -1
- package/dist/es2019/utils/index.js +3 -2
- package/dist/es2019/utils/prosemirror/autojoin.js +57 -0
- package/dist/es2019/version.json +1 -1
- package/dist/esm/i18n/cs.js +19 -0
- package/dist/esm/i18n/da.js +19 -0
- package/dist/esm/i18n/de.js +19 -0
- package/dist/esm/i18n/es.js +19 -0
- package/dist/esm/i18n/fi.js +19 -0
- package/dist/esm/i18n/fr.js +19 -0
- package/dist/esm/i18n/hu.js +19 -0
- package/dist/esm/i18n/it.js +19 -0
- package/dist/esm/i18n/ja.js +19 -0
- package/dist/esm/i18n/ko.js +19 -0
- package/dist/esm/i18n/nb.js +19 -0
- package/dist/esm/i18n/nl.js +19 -0
- package/dist/esm/i18n/pl.js +19 -0
- package/dist/esm/i18n/pt_BR.js +19 -0
- package/dist/esm/i18n/ru.js +19 -0
- package/dist/esm/i18n/sv.js +19 -0
- package/dist/esm/i18n/th.js +19 -0
- package/dist/esm/i18n/tr.js +19 -0
- package/dist/esm/i18n/uk.js +19 -0
- package/dist/esm/i18n/vi.js +19 -0
- package/dist/esm/i18n/zh.js +19 -0
- package/dist/esm/i18n/zh_TW.js +19 -0
- package/dist/esm/lists/analytics.js +32 -0
- package/dist/esm/lists/indentation.js +17 -0
- package/dist/esm/lists/index.js +6 -0
- package/dist/esm/lists/node.js +87 -0
- package/dist/esm/lists/replace-content.js +17 -0
- package/dist/esm/lists/selection.js +50 -0
- package/dist/esm/mark/commands.js +190 -0
- package/dist/esm/mark/index.js +1 -0
- package/dist/esm/messages/full-page.js +18 -0
- package/dist/esm/messages/index.js +1 -0
- package/dist/esm/monitoring/error.js +1 -1
- package/dist/esm/node-width/index.js +7 -0
- package/dist/esm/ui/DropList/index.js +1 -1
- package/dist/esm/ui/Toolbar/index.js +1 -0
- package/dist/esm/ui/index.js +2 -1
- package/dist/esm/ui-menu/DropdownMenu/index.js +15 -2
- package/dist/esm/ui-menu/ToolbarArrowKeyNavigationProvider/index.js +207 -0
- package/dist/esm/ui-menu/index.js +5 -4
- package/dist/esm/utils/commands.js +104 -2
- package/dist/esm/utils/document.js +25 -1
- package/dist/esm/utils/index.js +3 -2
- package/dist/esm/utils/prosemirror/autojoin.js +63 -0
- package/dist/esm/version.json +1 -1
- package/dist/types/i18n/cs.d.ts +19 -0
- package/dist/types/i18n/da.d.ts +19 -0
- package/dist/types/i18n/de.d.ts +19 -0
- package/dist/types/i18n/es.d.ts +19 -0
- package/dist/types/i18n/fi.d.ts +19 -0
- package/dist/types/i18n/fr.d.ts +19 -0
- package/dist/types/i18n/hu.d.ts +19 -0
- package/dist/types/i18n/it.d.ts +19 -0
- package/dist/types/i18n/ja.d.ts +19 -0
- package/dist/types/i18n/ko.d.ts +19 -0
- package/dist/types/i18n/nb.d.ts +19 -0
- package/dist/types/i18n/nl.d.ts +19 -0
- package/dist/types/i18n/pl.d.ts +19 -0
- package/dist/types/i18n/pt_BR.d.ts +19 -0
- package/dist/types/i18n/ru.d.ts +19 -0
- package/dist/types/i18n/sv.d.ts +19 -0
- package/dist/types/i18n/th.d.ts +19 -0
- package/dist/types/i18n/tr.d.ts +19 -0
- package/dist/types/i18n/uk.d.ts +19 -0
- package/dist/types/i18n/vi.d.ts +19 -0
- package/dist/types/i18n/zh.d.ts +19 -0
- package/dist/types/i18n/zh_TW.d.ts +19 -0
- package/dist/types/lists/analytics.d.ts +4 -0
- package/dist/types/lists/indentation.d.ts +5 -0
- package/dist/types/lists/index.d.ts +6 -0
- package/dist/types/lists/node.d.ts +18 -0
- package/dist/types/lists/replace-content.d.ts +8 -0
- package/dist/types/lists/selection.d.ts +13 -0
- package/dist/types/mark/commands.d.ts +18 -0
- package/dist/types/mark/index.d.ts +1 -0
- package/dist/types/messages/full-page.d.ts +17 -0
- package/dist/types/messages/index.d.ts +1 -0
- package/dist/types/node-width/index.d.ts +1 -0
- package/dist/types/ui/Toolbar/index.d.ts +5 -0
- package/dist/types/ui/index.d.ts +2 -0
- package/dist/types/ui-menu/DropdownMenu/index.d.ts +2 -1
- package/dist/types/ui-menu/ToolbarArrowKeyNavigationProvider/index.d.ts +30 -0
- package/dist/types/ui-menu/index.d.ts +7 -6
- package/dist/types/utils/commands.d.ts +36 -2
- package/dist/types/utils/document.d.ts +4 -0
- package/dist/types/utils/index.d.ts +4 -2
- package/dist/types/utils/prosemirror/autojoin.d.ts +13 -0
- package/dist/types-ts4.5/i18n/cs.d.ts +19 -0
- package/dist/types-ts4.5/i18n/da.d.ts +19 -0
- package/dist/types-ts4.5/i18n/de.d.ts +19 -0
- package/dist/types-ts4.5/i18n/es.d.ts +19 -0
- package/dist/types-ts4.5/i18n/fi.d.ts +19 -0
- package/dist/types-ts4.5/i18n/fr.d.ts +19 -0
- package/dist/types-ts4.5/i18n/hu.d.ts +19 -0
- package/dist/types-ts4.5/i18n/it.d.ts +19 -0
- package/dist/types-ts4.5/i18n/ja.d.ts +19 -0
- package/dist/types-ts4.5/i18n/ko.d.ts +19 -0
- package/dist/types-ts4.5/i18n/nb.d.ts +19 -0
- package/dist/types-ts4.5/i18n/nl.d.ts +19 -0
- package/dist/types-ts4.5/i18n/pl.d.ts +19 -0
- package/dist/types-ts4.5/i18n/pt_BR.d.ts +19 -0
- package/dist/types-ts4.5/i18n/ru.d.ts +19 -0
- package/dist/types-ts4.5/i18n/sv.d.ts +19 -0
- package/dist/types-ts4.5/i18n/th.d.ts +19 -0
- package/dist/types-ts4.5/i18n/tr.d.ts +19 -0
- package/dist/types-ts4.5/i18n/uk.d.ts +19 -0
- package/dist/types-ts4.5/i18n/vi.d.ts +19 -0
- package/dist/types-ts4.5/i18n/zh.d.ts +19 -0
- package/dist/types-ts4.5/i18n/zh_TW.d.ts +19 -0
- package/dist/types-ts4.5/lists/analytics.d.ts +4 -0
- package/dist/types-ts4.5/lists/indentation.d.ts +5 -0
- package/dist/types-ts4.5/lists/index.d.ts +6 -0
- package/dist/types-ts4.5/lists/node.d.ts +18 -0
- package/dist/types-ts4.5/lists/replace-content.d.ts +8 -0
- package/dist/types-ts4.5/lists/selection.d.ts +13 -0
- package/dist/types-ts4.5/mark/commands.d.ts +18 -0
- package/dist/types-ts4.5/mark/index.d.ts +1 -0
- package/dist/types-ts4.5/messages/full-page.d.ts +17 -0
- package/dist/types-ts4.5/messages/index.d.ts +1 -0
- package/dist/types-ts4.5/node-width/index.d.ts +1 -0
- package/dist/types-ts4.5/ui/Toolbar/index.d.ts +5 -0
- package/dist/types-ts4.5/ui/index.d.ts +2 -0
- package/dist/types-ts4.5/ui-menu/DropdownMenu/index.d.ts +2 -1
- package/dist/types-ts4.5/ui-menu/ToolbarArrowKeyNavigationProvider/index.d.ts +30 -0
- package/dist/types-ts4.5/ui-menu/index.d.ts +7 -6
- package/dist/types-ts4.5/utils/commands.d.ts +42 -2
- package/dist/types-ts4.5/utils/document.d.ts +4 -0
- package/dist/types-ts4.5/utils/index.d.ts +4 -2
- package/dist/types-ts4.5/utils/prosemirror/autojoin.d.ts +13 -0
- package/lists/package.json +15 -0
- package/mark/package.json +15 -0
- package/package.json +6 -3
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
2
|
+
// eslint-disable-next-line no-duplicate-imports
|
|
3
|
+
|
|
4
|
+
import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
|
|
5
|
+
const SMART_TO_ASCII = {
|
|
6
|
+
'…': '...',
|
|
7
|
+
'→': '->',
|
|
8
|
+
'←': '<-',
|
|
9
|
+
'–': '--',
|
|
10
|
+
'“': '"',
|
|
11
|
+
'”': '"',
|
|
12
|
+
'‘': "'",
|
|
13
|
+
'’': "'"
|
|
14
|
+
};
|
|
15
|
+
const FIND_SMART_CHAR = new RegExp(`[${Object.keys(SMART_TO_ASCII).join('')}]`, 'g');
|
|
16
|
+
const isNodeTextBlock = schema => {
|
|
17
|
+
const {
|
|
18
|
+
mention,
|
|
19
|
+
text,
|
|
20
|
+
emoji
|
|
21
|
+
} = schema.nodes;
|
|
22
|
+
return (node, _, parent) => {
|
|
23
|
+
if (node.type === mention || node.type === emoji || node.type === text) {
|
|
24
|
+
return parent === null || parent === void 0 ? void 0 : parent.isTextblock;
|
|
25
|
+
}
|
|
26
|
+
return;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
const replaceSmartCharsToAscii = (position, textContent, tr) => {
|
|
30
|
+
const {
|
|
31
|
+
schema
|
|
32
|
+
} = tr.doc.type;
|
|
33
|
+
let match;
|
|
34
|
+
while (match = FIND_SMART_CHAR.exec(textContent)) {
|
|
35
|
+
const {
|
|
36
|
+
0: smartChar,
|
|
37
|
+
index: offset
|
|
38
|
+
} = match;
|
|
39
|
+
const replacePos = tr.mapping.map(position + offset);
|
|
40
|
+
const replacementText = schema.text(SMART_TO_ASCII[smartChar]);
|
|
41
|
+
tr.replaceWith(replacePos, replacePos + smartChar.length, replacementText);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const replaceMentionOrEmojiForTextContent = (position, nodeSize, textContent, tr) => {
|
|
45
|
+
const currentPos = tr.mapping.map(position);
|
|
46
|
+
const {
|
|
47
|
+
schema
|
|
48
|
+
} = tr.doc.type;
|
|
49
|
+
tr.replaceWith(currentPos, currentPos + nodeSize, schema.text(textContent));
|
|
50
|
+
};
|
|
51
|
+
export function filterChildrenBetween(doc, from, to, predicate) {
|
|
52
|
+
const results = [];
|
|
53
|
+
doc.nodesBetween(from, to, (node, pos, parent) => {
|
|
54
|
+
if (predicate(node, pos, parent)) {
|
|
55
|
+
results.push({
|
|
56
|
+
node,
|
|
57
|
+
pos
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return results;
|
|
62
|
+
}
|
|
63
|
+
export const transformSmartCharsMentionsAndEmojis = (from, to, tr) => {
|
|
64
|
+
const {
|
|
65
|
+
schema
|
|
66
|
+
} = tr.doc.type;
|
|
67
|
+
const {
|
|
68
|
+
mention,
|
|
69
|
+
text,
|
|
70
|
+
emoji
|
|
71
|
+
} = schema.nodes;
|
|
72
|
+
// Traverse through all the nodes within the range and replace them with their plaintext counterpart
|
|
73
|
+
const children = filterChildrenBetween(tr.doc, from, to, isNodeTextBlock(schema));
|
|
74
|
+
children.forEach(({
|
|
75
|
+
node,
|
|
76
|
+
pos
|
|
77
|
+
}) => {
|
|
78
|
+
if (node.type === mention || node.type === emoji) {
|
|
79
|
+
// Convert gracefully when no text found, ProseMirror will blow up if you try to create a node with an empty string or undefined
|
|
80
|
+
let replacementText = node.attrs.text;
|
|
81
|
+
if (typeof replacementText === 'undefined' || replacementText === '') {
|
|
82
|
+
replacementText = `${node.type.name} text missing`;
|
|
83
|
+
}
|
|
84
|
+
replaceMentionOrEmojiForTextContent(pos, node.nodeSize, replacementText, tr);
|
|
85
|
+
} else if (node.type === text && node.text) {
|
|
86
|
+
const replacePosition = pos > from ? pos : from;
|
|
87
|
+
const textToReplace = pos > from ? node.text : node.text.substr(from - pos);
|
|
88
|
+
replaceSmartCharsToAscii(replacePosition, textToReplace, tr);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
export const applyMarkOnRange = (from, to, removeMark, mark, tr) => {
|
|
93
|
+
const {
|
|
94
|
+
schema
|
|
95
|
+
} = tr.doc.type;
|
|
96
|
+
const {
|
|
97
|
+
code
|
|
98
|
+
} = schema.marks;
|
|
99
|
+
if (mark.type === code) {
|
|
100
|
+
transformSmartCharsMentionsAndEmojis(from, to, tr);
|
|
101
|
+
}
|
|
102
|
+
tr.doc.nodesBetween(tr.mapping.map(from), tr.mapping.map(to), (node, pos) => {
|
|
103
|
+
if (!node.isText) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// This is an issue when the user selects some text.
|
|
108
|
+
// We need to check if the current node position is less than the range selection from.
|
|
109
|
+
// If it’s true, that means we should apply the mark using the range selection,
|
|
110
|
+
// not the current node position.
|
|
111
|
+
const nodeBetweenFrom = Math.max(pos, tr.mapping.map(from));
|
|
112
|
+
const nodeBetweenTo = Math.min(pos + node.nodeSize, tr.mapping.map(to));
|
|
113
|
+
if (removeMark) {
|
|
114
|
+
tr.removeMark(nodeBetweenFrom, nodeBetweenTo, mark);
|
|
115
|
+
} else {
|
|
116
|
+
tr.addMark(nodeBetweenFrom, nodeBetweenTo, mark);
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
});
|
|
120
|
+
return tr;
|
|
121
|
+
};
|
|
122
|
+
const entireSelectionContainsMark = (mark, doc, fromPos, toPos) => {
|
|
123
|
+
let onlyContainsMark = true;
|
|
124
|
+
doc.nodesBetween(fromPos, toPos, node => {
|
|
125
|
+
// Skip recursion once we've found text which doesn't include the mark
|
|
126
|
+
if (!onlyContainsMark) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
if (node.isText) {
|
|
130
|
+
onlyContainsMark && (onlyContainsMark = mark.isInSet(node.marks));
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
return onlyContainsMark;
|
|
134
|
+
};
|
|
135
|
+
const toggleMarkInRange = mark => (state, dispatch) => {
|
|
136
|
+
const {
|
|
137
|
+
tr
|
|
138
|
+
} = state;
|
|
139
|
+
if (state.selection instanceof CellSelection) {
|
|
140
|
+
let removeMark = true;
|
|
141
|
+
const cells = [];
|
|
142
|
+
state.selection.forEachCell((cell, cellPos) => {
|
|
143
|
+
cells.push({
|
|
144
|
+
node: cell,
|
|
145
|
+
pos: cellPos
|
|
146
|
+
});
|
|
147
|
+
const from = cellPos;
|
|
148
|
+
const to = cellPos + cell.nodeSize;
|
|
149
|
+
removeMark && (removeMark = entireSelectionContainsMark(mark, state.doc, from, to));
|
|
150
|
+
});
|
|
151
|
+
for (let i = cells.length - 1; i >= 0; i--) {
|
|
152
|
+
const cell = cells[i];
|
|
153
|
+
const from = cell.pos;
|
|
154
|
+
const to = from + cell.node.nodeSize;
|
|
155
|
+
applyMarkOnRange(from, to, removeMark, mark, tr);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
const {
|
|
159
|
+
$from,
|
|
160
|
+
$to
|
|
161
|
+
} = state.selection;
|
|
162
|
+
// We decide to remove the mark only if the entire selection contains the mark
|
|
163
|
+
// Examples with *bold* text
|
|
164
|
+
// Scenario 1: Selection contains both bold and non-bold text -> bold entire selection
|
|
165
|
+
// Scenario 2: Selection contains only bold text -> un-bold entire selection
|
|
166
|
+
// Scenario 3: Selection contains no bold text -> bold entire selection
|
|
167
|
+
const removeMark = entireSelectionContainsMark(mark, state.doc, $from.pos, $to.pos);
|
|
168
|
+
applyMarkOnRange($from.pos, $to.pos, removeMark, mark, tr);
|
|
169
|
+
}
|
|
170
|
+
if (tr.docChanged) {
|
|
171
|
+
if (dispatch) {
|
|
172
|
+
dispatch(tr);
|
|
173
|
+
}
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* A wrapper over the default toggleMark, except when we have a selection
|
|
181
|
+
* we only toggle marks on text nodes rather than inline nodes.
|
|
182
|
+
* @param markType
|
|
183
|
+
* @param attrs
|
|
184
|
+
*/
|
|
185
|
+
export const toggleMark = (markType, attrs) => (state, dispatch) => {
|
|
186
|
+
const mark = markType.create(attrs);
|
|
187
|
+
|
|
188
|
+
// For cursor selections we can use the default behaviour.
|
|
189
|
+
if (state.selection instanceof TextSelection && state.selection.$cursor) {
|
|
190
|
+
const {
|
|
191
|
+
tr
|
|
192
|
+
} = state;
|
|
193
|
+
if (mark.isInSet(state.storedMarks || state.selection.$cursor.marks())) {
|
|
194
|
+
tr.removeStoredMark(mark);
|
|
195
|
+
} else {
|
|
196
|
+
tr.addStoredMark(mark);
|
|
197
|
+
}
|
|
198
|
+
if (dispatch) {
|
|
199
|
+
dispatch(tr);
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
return toggleMarkInRange(mark)(state, dispatch);
|
|
205
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { transformSmartCharsMentionsAndEmojis, applyMarkOnRange, toggleMark, filterChildrenBetween } from './commands';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineMessages } from 'react-intl-next';
|
|
2
|
+
export const messages = defineMessages({
|
|
3
|
+
toolbarLabel: {
|
|
4
|
+
id: 'fabric.editor.toolbarLabel',
|
|
5
|
+
defaultMessage: 'Editor toolbar',
|
|
6
|
+
description: 'Label for the ARIA region landmark'
|
|
7
|
+
},
|
|
8
|
+
pageActionsLabel: {
|
|
9
|
+
id: 'fabric.editor.pageActionsLabel',
|
|
10
|
+
defaultMessage: 'Page actions',
|
|
11
|
+
description: 'Label for the ARIA region landmark'
|
|
12
|
+
},
|
|
13
|
+
editableContentLabel: {
|
|
14
|
+
id: 'fabric.editor.editableContentLabel',
|
|
15
|
+
defaultMessage: 'Editable content',
|
|
16
|
+
description: 'Label for the ARIA region landmark'
|
|
17
|
+
}
|
|
18
|
+
});
|
|
@@ -7,6 +7,7 @@ export { codeBlockButtonMessages } from './codeBlockButton';
|
|
|
7
7
|
export { toolbarInsertBlockMessages } from './insert-block';
|
|
8
8
|
export { toolbarMessages as mediaAndEmbedToolbarMessages } from './media-and-embed-toolbar';
|
|
9
9
|
export { messages as cardMessages } from './card';
|
|
10
|
+
export { messages as fullPageMessages } from './full-page';
|
|
10
11
|
export default defineMessages({
|
|
11
12
|
layoutFixedWidth: {
|
|
12
13
|
id: 'fabric.editor.layoutFixedWidth',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
|
|
2
2
|
const packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
|
|
3
|
-
const packageVersion = "74.
|
|
3
|
+
const packageVersion = "74.31.0";
|
|
4
4
|
const sanitiseSentryEvents = (data, _hint) => {
|
|
5
5
|
// Remove URL as it has UGC
|
|
6
6
|
// TODO: Sanitise the URL instead of just removing it
|
|
@@ -102,4 +102,11 @@ export const getTableContainerWidth = node => {
|
|
|
102
102
|
return node.attrs.width;
|
|
103
103
|
}
|
|
104
104
|
return layoutToWidth[node.attrs.layout];
|
|
105
|
+
};
|
|
106
|
+
export const getTableWidthWithNumberColumn = (node, offset) => {
|
|
107
|
+
const isNumberColumnEnabled = node.attrs.isNumberColumnEnabled;
|
|
108
|
+
if (isNumberColumnEnabled && offset > 0) {
|
|
109
|
+
return getTableContainerWidth(node) - offset;
|
|
110
|
+
}
|
|
111
|
+
return getTableContainerWidth(node);
|
|
105
112
|
};
|
|
@@ -8,7 +8,7 @@ import { themed } from '@atlaskit/theme/components';
|
|
|
8
8
|
import { borderRadius } from '@atlaskit/theme/constants';
|
|
9
9
|
import Layer from '../Layer';
|
|
10
10
|
const packageName = "@atlaskit/editor-common";
|
|
11
|
-
const packageVersion = "74.
|
|
11
|
+
const packageVersion = "74.31.0";
|
|
12
12
|
const halfFocusRing = 1;
|
|
13
13
|
const dropOffset = '0, 8';
|
|
14
14
|
class DropList extends Component {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const EDIT_AREA_ID = 'ak-editor-textarea';
|
package/dist/es2019/ui/index.js
CHANGED
|
@@ -25,4 +25,5 @@ export { snapTo, handleSides, imageAlignmentMap } from './ResizerLegacy/utils';
|
|
|
25
25
|
export { wrapperStyle } from './ResizerLegacy/styled';
|
|
26
26
|
export { panelTextInput } from './PanelTextInput/styles';
|
|
27
27
|
export { default as PanelTextInput } from './PanelTextInput';
|
|
28
|
-
export { default as Announcer } from './Announcer/announcer';
|
|
28
|
+
export { default as Announcer } from './Announcer/announcer';
|
|
29
|
+
export { EDIT_AREA_ID } from './Toolbar';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
2
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
3
3
|
/** @jsx jsx */
|
|
4
|
-
import React, { PureComponent } from 'react';
|
|
4
|
+
import React, { PureComponent, useContext } from 'react';
|
|
5
5
|
import { css, jsx } from '@emotion/react';
|
|
6
6
|
import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
|
|
7
7
|
import { CustomItem, MenuGroup } from '@atlaskit/menu';
|
|
@@ -10,6 +10,7 @@ import { B100, DN600, DN80, N70, N900 } from '@atlaskit/theme/colors';
|
|
|
10
10
|
import { themed } from '@atlaskit/theme/components';
|
|
11
11
|
import Tooltip from '@atlaskit/tooltip';
|
|
12
12
|
import { DropdownMenuSharedCssClassName } from '../../styles';
|
|
13
|
+
import { KeyDownHandlerContext } from '../../ui-menu/ToolbarArrowKeyNavigationProvider';
|
|
13
14
|
import { withReactEditorViewOuterListeners } from '../../ui-react';
|
|
14
15
|
import DropList from '../../ui/DropList';
|
|
15
16
|
import Popup from '../../ui/Popup';
|
|
@@ -319,4 +320,17 @@ function DropdownMenuItem({
|
|
|
319
320
|
}, dropListItem);
|
|
320
321
|
}
|
|
321
322
|
return dropListItem;
|
|
322
|
-
}
|
|
323
|
+
}
|
|
324
|
+
export const DropdownMenuWithKeyboardNavigation = /*#__PURE__*/React.memo(({
|
|
325
|
+
...props
|
|
326
|
+
}) => {
|
|
327
|
+
const keyDownHandlerContext = useContext(KeyDownHandlerContext);
|
|
328
|
+
//This context is to handle the tab, Arrow Right/Left key events for dropdown.
|
|
329
|
+
//Default context has the void callbacks for above key events
|
|
330
|
+
return jsx(DropdownMenuWrapper, _extends({
|
|
331
|
+
arrowKeyNavigationProviderOptions: {
|
|
332
|
+
...props.arrowKeyNavigationProviderOptions,
|
|
333
|
+
keyDownHandlerContext
|
|
334
|
+
}
|
|
335
|
+
}, props));
|
|
336
|
+
});
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
import React, { useCallback, useLayoutEffect, useRef } from 'react';
|
|
4
|
+
import { css, jsx } from '@emotion/react';
|
|
5
|
+
import { fullPageMessages as messages } from '../../messages';
|
|
6
|
+
import { EDIT_AREA_ID } from '../../ui';
|
|
7
|
+
/*
|
|
8
|
+
** The context is used to handle the keydown events of submenus.
|
|
9
|
+
** Because the keyboard navigation is explicitly managed for main toolbar items
|
|
10
|
+
** Few key presses such as Tab,Arrow Right/Left need ot be handled here via context
|
|
11
|
+
*/
|
|
12
|
+
export const KeyDownHandlerContext = /*#__PURE__*/React.createContext({
|
|
13
|
+
handleArrowLeft: () => {},
|
|
14
|
+
handleArrowRight: () => {},
|
|
15
|
+
handleTab: () => {}
|
|
16
|
+
});
|
|
17
|
+
const centeredToolbarContainer = css`
|
|
18
|
+
display: flex;
|
|
19
|
+
width: 100%;
|
|
20
|
+
align-items: center;
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* This component is a wrapper of main toolbar which listens to keydown events of children
|
|
25
|
+
* and handles left/right arrow key navigation for all focusable elements
|
|
26
|
+
* @param
|
|
27
|
+
* @returns
|
|
28
|
+
*/
|
|
29
|
+
export const ToolbarArrowKeyNavigationProvider = ({
|
|
30
|
+
children,
|
|
31
|
+
editorView,
|
|
32
|
+
childComponentSelector,
|
|
33
|
+
handleEscape,
|
|
34
|
+
disableArrowKeyNavigation,
|
|
35
|
+
isShortcutToFocusToolbar,
|
|
36
|
+
editorAppearance,
|
|
37
|
+
useStickyToolbar,
|
|
38
|
+
intl
|
|
39
|
+
}) => {
|
|
40
|
+
const wrapperRef = useRef(null);
|
|
41
|
+
const selectedItemIndex = useRef(0);
|
|
42
|
+
const incrementIndex = useCallback(list => {
|
|
43
|
+
let index = 0;
|
|
44
|
+
if (document.activeElement) {
|
|
45
|
+
index = list.indexOf(document.activeElement);
|
|
46
|
+
index = (index + 1) % list.length;
|
|
47
|
+
}
|
|
48
|
+
selectedItemIndex.current = index;
|
|
49
|
+
}, []);
|
|
50
|
+
const decrementIndex = useCallback(list => {
|
|
51
|
+
let index = 0;
|
|
52
|
+
if (document.activeElement) {
|
|
53
|
+
index = list.indexOf(document.activeElement);
|
|
54
|
+
index = (list.length + index - 1) % list.length;
|
|
55
|
+
}
|
|
56
|
+
selectedItemIndex.current = index;
|
|
57
|
+
}, []);
|
|
58
|
+
const handleArrowRight = () => {
|
|
59
|
+
var _filteredFocusableEle;
|
|
60
|
+
const filteredFocusableElements = getFilteredFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
|
|
61
|
+
incrementIndex(filteredFocusableElements);
|
|
62
|
+
(_filteredFocusableEle = filteredFocusableElements[selectedItemIndex.current]) === null || _filteredFocusableEle === void 0 ? void 0 : _filteredFocusableEle.focus();
|
|
63
|
+
};
|
|
64
|
+
const handleArrowLeft = () => {
|
|
65
|
+
var _filteredFocusableEle2;
|
|
66
|
+
const filteredFocusableElements = getFilteredFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
|
|
67
|
+
decrementIndex(filteredFocusableElements);
|
|
68
|
+
(_filteredFocusableEle2 = filteredFocusableElements[selectedItemIndex.current]) === null || _filteredFocusableEle2 === void 0 ? void 0 : _filteredFocusableEle2.focus();
|
|
69
|
+
};
|
|
70
|
+
const handleTab = () => {
|
|
71
|
+
var _filteredFocusableEle3;
|
|
72
|
+
const filteredFocusableElements = getFilteredFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
|
|
73
|
+
(_filteredFocusableEle3 = filteredFocusableElements[selectedItemIndex.current]) === null || _filteredFocusableEle3 === void 0 ? void 0 : _filteredFocusableEle3.focus();
|
|
74
|
+
};
|
|
75
|
+
const handleTabLocal = () => {
|
|
76
|
+
const filteredFocusableElements = getFilteredFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
|
|
77
|
+
filteredFocusableElements.forEach(element => {
|
|
78
|
+
element.setAttribute('tabindex', '-1');
|
|
79
|
+
});
|
|
80
|
+
filteredFocusableElements[selectedItemIndex.current].setAttribute('tabindex', '0');
|
|
81
|
+
};
|
|
82
|
+
const focusAndScrollToElement = (element, scrollToElement = true) => {
|
|
83
|
+
if (scrollToElement) {
|
|
84
|
+
element === null || element === void 0 ? void 0 : element.scrollIntoView({
|
|
85
|
+
behavior: 'smooth',
|
|
86
|
+
block: 'center',
|
|
87
|
+
inline: 'nearest'
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
element.focus();
|
|
91
|
+
};
|
|
92
|
+
const submenuKeydownHandleContext = {
|
|
93
|
+
handleArrowLeft,
|
|
94
|
+
handleArrowRight,
|
|
95
|
+
handleTab
|
|
96
|
+
};
|
|
97
|
+
useLayoutEffect(() => {
|
|
98
|
+
if (!wrapperRef.current || disableArrowKeyNavigation) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const {
|
|
102
|
+
current: element
|
|
103
|
+
} = wrapperRef;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* To handle the key events on the list
|
|
107
|
+
* @param event
|
|
108
|
+
*/
|
|
109
|
+
const handleKeyDown = event => {
|
|
110
|
+
var _document$querySelect, _document$querySelect2, _wrapperRef$current;
|
|
111
|
+
//To trap the focus inside the horizontal toolbar for left and right arrow keys
|
|
112
|
+
const targetElement = event.target;
|
|
113
|
+
|
|
114
|
+
//To filter out the events outside the child component
|
|
115
|
+
if (!targetElement.closest(`${childComponentSelector}`)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
//The key events are from child components such as dropdown menus / popups are ignored
|
|
120
|
+
if ((_document$querySelect = document.querySelector('[data-role="droplistContent"], [data-test-id="color-picker-menu"], [data-emoji-picker-container="true"]')) !== null && _document$querySelect !== void 0 && _document$querySelect.contains(targetElement) || (_document$querySelect2 = document.querySelector('[data-test-id="color-picker-menu"]')) !== null && _document$querySelect2 !== void 0 && _document$querySelect2.contains(targetElement) || event.key === 'ArrowUp' || event.key === 'ArrowDown' || disableArrowKeyNavigation) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const menuWrapper = document.querySelector('.menu-key-handler-wrapper');
|
|
124
|
+
if (menuWrapper) {
|
|
125
|
+
// if menu wrapper exists, then a menu is open and arrow keys will be handled by MenuArrowKeyNavigationProvider
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const filteredFocusableElements = getFilteredFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
|
|
129
|
+
if (!filteredFocusableElements || (filteredFocusableElements === null || filteredFocusableElements === void 0 ? void 0 : filteredFocusableElements.length) === 0) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
//This is kind of hack to reset the current focused toolbar item
|
|
134
|
+
//to handle some use cases such as Tab in/out of main toolbar
|
|
135
|
+
if (!((_wrapperRef$current = wrapperRef.current) !== null && _wrapperRef$current !== void 0 && _wrapperRef$current.contains(targetElement))) {
|
|
136
|
+
selectedItemIndex.current = -1;
|
|
137
|
+
} else {
|
|
138
|
+
selectedItemIndex.current = filteredFocusableElements.indexOf(targetElement) > -1 ? filteredFocusableElements.indexOf(targetElement) : selectedItemIndex.current;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
//do not scroll to focused element for sticky toolbar when navigating with arrows to avoid unnesessary scroll jump
|
|
142
|
+
const allowScrollToElement = !(editorAppearance === 'comment' && !!useStickyToolbar);
|
|
143
|
+
switch (event.key) {
|
|
144
|
+
case 'ArrowRight':
|
|
145
|
+
incrementIndex(filteredFocusableElements);
|
|
146
|
+
focusAndScrollToElement(filteredFocusableElements[selectedItemIndex.current], allowScrollToElement);
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
break;
|
|
149
|
+
case 'ArrowLeft':
|
|
150
|
+
decrementIndex(filteredFocusableElements);
|
|
151
|
+
focusAndScrollToElement(filteredFocusableElements[selectedItemIndex.current], allowScrollToElement);
|
|
152
|
+
event.preventDefault();
|
|
153
|
+
break;
|
|
154
|
+
case 'Tab':
|
|
155
|
+
handleTabLocal();
|
|
156
|
+
break;
|
|
157
|
+
case 'Escape':
|
|
158
|
+
handleEscape(event);
|
|
159
|
+
break;
|
|
160
|
+
default:
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const globalKeyDownHandler = event => {
|
|
164
|
+
//To focus the first element in the toolbar
|
|
165
|
+
if (isShortcutToFocusToolbar(event)) {
|
|
166
|
+
var _filteredFocusableEle4, _filteredFocusableEle5;
|
|
167
|
+
const filteredFocusableElements = getFilteredFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
|
|
168
|
+
(_filteredFocusableEle4 = filteredFocusableElements[0]) === null || _filteredFocusableEle4 === void 0 ? void 0 : _filteredFocusableEle4.focus();
|
|
169
|
+
(_filteredFocusableEle5 = filteredFocusableElements[0]) === null || _filteredFocusableEle5 === void 0 ? void 0 : _filteredFocusableEle5.scrollIntoView({
|
|
170
|
+
behavior: 'smooth',
|
|
171
|
+
block: 'center',
|
|
172
|
+
inline: 'nearest'
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
element === null || element === void 0 ? void 0 : element.addEventListener('keydown', handleKeyDown);
|
|
177
|
+
const editorViewDom = editorView === null || editorView === void 0 ? void 0 : editorView.dom;
|
|
178
|
+
if (isShortcutToFocusToolbar) {
|
|
179
|
+
editorViewDom === null || editorViewDom === void 0 ? void 0 : editorViewDom.addEventListener('keydown', globalKeyDownHandler);
|
|
180
|
+
}
|
|
181
|
+
return () => {
|
|
182
|
+
element === null || element === void 0 ? void 0 : element.removeEventListener('keydown', handleKeyDown);
|
|
183
|
+
if (isShortcutToFocusToolbar) {
|
|
184
|
+
editorViewDom === null || editorViewDom === void 0 ? void 0 : editorViewDom.removeEventListener('keydown', globalKeyDownHandler);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}, [selectedItemIndex, wrapperRef, editorView, disableArrowKeyNavigation, handleEscape, childComponentSelector, incrementIndex, decrementIndex, isShortcutToFocusToolbar, editorAppearance, useStickyToolbar]);
|
|
188
|
+
return jsx("div", {
|
|
189
|
+
css: editorAppearance === 'comment' && centeredToolbarContainer,
|
|
190
|
+
className: "custom-key-handler-wrapper",
|
|
191
|
+
ref: wrapperRef,
|
|
192
|
+
role: "toolbar",
|
|
193
|
+
"aria-label": intl.formatMessage(messages.toolbarLabel),
|
|
194
|
+
"aria-controls": EDIT_AREA_ID
|
|
195
|
+
}, jsx(KeyDownHandlerContext.Provider, {
|
|
196
|
+
value: submenuKeydownHandleContext
|
|
197
|
+
}, children));
|
|
198
|
+
};
|
|
199
|
+
function getFocusableElements(rootNode) {
|
|
200
|
+
if (!rootNode) {
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
const focusableModalElements = rootNode.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, div[tabindex="-1"], div[tabindex="0"]') || [];
|
|
204
|
+
return Array.from(focusableModalElements);
|
|
205
|
+
}
|
|
206
|
+
function getFilteredFocusableElements(rootNode) {
|
|
207
|
+
//The focusable elements from child components such as dropdown menus / popups are ignored
|
|
208
|
+
return getFocusableElements(rootNode).filter(elm => !elm.closest('[data-role="droplistContent"]') && !elm.closest('[data-emoji-picker-container="true"]') && !elm.closest('[data-test-id="color-picker-menu"]') && !elm.closest('.scroll-buttons'));
|
|
209
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export { default as DropdownMenu } from '
|
|
2
|
-
export { default as ToolbarButton, TOOLBAR_BUTTON } from '
|
|
3
|
-
export { ArrowKeyNavigationProvider } from '
|
|
4
|
-
export {
|
|
1
|
+
export { default as DropdownMenu, DropdownMenuWithKeyboardNavigation } from './DropdownMenu';
|
|
2
|
+
export { default as ToolbarButton, TOOLBAR_BUTTON } from './ToolbarButton';
|
|
3
|
+
export { ArrowKeyNavigationProvider } from './ArrowKeyNavigationProvider';
|
|
4
|
+
export { ToolbarArrowKeyNavigationProvider, KeyDownHandlerContext } from './ToolbarArrowKeyNavigationProvider';
|
|
5
|
+
export { ArrowKeyNavigationType } from './ArrowKeyNavigationProvider/types';
|
|
5
6
|
export { ColorPaletteArrowKeyNavigationProvider } from './ArrowKeyNavigationProvider/ColorPaletteArrowKeyNavigationProvider';
|
|
6
7
|
export { default as Dropdown } from './Dropdown';
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
2
|
+
import { GapCursorSelection } from '../selection';
|
|
3
|
+
import { isEmptyParagraph } from './editor-core-utils';
|
|
4
|
+
const filter = (predicates, cmd) => {
|
|
2
5
|
return function (state, dispatch, view) {
|
|
3
6
|
if (!Array.isArray(predicates)) {
|
|
4
7
|
predicates = [predicates];
|
|
@@ -8,4 +11,105 @@ export const filter = (predicates, cmd) => {
|
|
|
8
11
|
}
|
|
9
12
|
return cmd(state, dispatch, view) || false;
|
|
10
13
|
};
|
|
11
|
-
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Walk forwards from a position until we encounter the (inside) start of
|
|
18
|
+
* the next node, or reach the end of the document.
|
|
19
|
+
*
|
|
20
|
+
* @param $startPos Position to start walking from.
|
|
21
|
+
*/
|
|
22
|
+
export const walkNextNode = $startPos => {
|
|
23
|
+
let $pos = $startPos;
|
|
24
|
+
|
|
25
|
+
// invariant 1: don't walk past the end of the document
|
|
26
|
+
// invariant 2: we are at the beginning or
|
|
27
|
+
// we haven't walked to the start of *any* node
|
|
28
|
+
// parentOffset includes textOffset.
|
|
29
|
+
while ($pos.pos < $pos.doc.nodeSize - 2 && ($pos.pos === $startPos.pos || $pos.parentOffset > 0)) {
|
|
30
|
+
$pos = $pos.doc.resolve($pos.pos + 1);
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
$pos: $pos,
|
|
34
|
+
foundNode: $pos.pos < $pos.doc.nodeSize - 2
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Walk backwards from a position until we encounter the (inside) end of
|
|
40
|
+
* the previous node, or reach the start of the document.
|
|
41
|
+
*
|
|
42
|
+
* @param $startPos Position to start walking from.
|
|
43
|
+
*/
|
|
44
|
+
export const walkPrevNode = $startPos => {
|
|
45
|
+
let $pos = $startPos;
|
|
46
|
+
while ($pos.pos > 0 && ($pos.pos === $startPos.pos || $pos.parentOffset < $pos.parent.nodeSize - 2)) {
|
|
47
|
+
$pos = $pos.doc.resolve($pos.pos - 1);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
$pos: $pos,
|
|
51
|
+
foundNode: $pos.pos > 0
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* If the selection is empty, is inside a paragraph node and `canNextNodeMoveUp` is true then delete current paragraph
|
|
57
|
+
* and move the node below it up. The selection will be retained, to be placed in the moved node.
|
|
58
|
+
*
|
|
59
|
+
* @param canNextNodeMoveUp check if node directly after the selection is able to be brought up to selection
|
|
60
|
+
* @returns PM Command
|
|
61
|
+
*/
|
|
62
|
+
export const deleteEmptyParagraphAndMoveBlockUp = canNextNodeMoveUp => {
|
|
63
|
+
return (state, dispatch, view) => {
|
|
64
|
+
const {
|
|
65
|
+
selection: {
|
|
66
|
+
$from: {
|
|
67
|
+
pos,
|
|
68
|
+
parent
|
|
69
|
+
},
|
|
70
|
+
$head,
|
|
71
|
+
empty
|
|
72
|
+
},
|
|
73
|
+
tr,
|
|
74
|
+
doc
|
|
75
|
+
} = state;
|
|
76
|
+
const {
|
|
77
|
+
$pos
|
|
78
|
+
} = walkNextNode($head);
|
|
79
|
+
const nextPMNode = doc.nodeAt($pos.pos - 1);
|
|
80
|
+
if (empty && nextPMNode && canNextNodeMoveUp(nextPMNode) && isEmptyParagraph(parent) && view !== null && view !== void 0 && view.endOfTextblock('right')) {
|
|
81
|
+
tr.deleteRange(pos - 1, pos + 1);
|
|
82
|
+
if (dispatch) {
|
|
83
|
+
dispatch(tr);
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
export const insertContentDeleteRange = (tr, getSelectionResolvedPos, insertions, deletions) => {
|
|
91
|
+
insertions.forEach(contentInsert => {
|
|
92
|
+
let [content, pos] = contentInsert;
|
|
93
|
+
tr.insert(tr.mapping.map(pos), content);
|
|
94
|
+
});
|
|
95
|
+
deletions.forEach(deleteRange => {
|
|
96
|
+
let [firstPos, lastPos] = deleteRange;
|
|
97
|
+
tr.delete(tr.mapping.map(firstPos), tr.mapping.map(lastPos));
|
|
98
|
+
});
|
|
99
|
+
tr.setSelection(new TextSelection(getSelectionResolvedPos(tr)));
|
|
100
|
+
};
|
|
101
|
+
export const isEmptySelectionAtStart = state => {
|
|
102
|
+
const {
|
|
103
|
+
empty,
|
|
104
|
+
$from
|
|
105
|
+
} = state.selection;
|
|
106
|
+
return empty && ($from.parentOffset === 0 || state.selection instanceof GapCursorSelection);
|
|
107
|
+
};
|
|
108
|
+
export const isEmptySelectionAtEnd = state => {
|
|
109
|
+
const {
|
|
110
|
+
empty,
|
|
111
|
+
$from
|
|
112
|
+
} = state.selection;
|
|
113
|
+
return empty && ($from.end() === $from.pos || state.selection instanceof GapCursorSelection);
|
|
114
|
+
};
|
|
115
|
+
export { filter as filterCommand };
|