@skbkontur/markdown 2.7.0-alpha.1 → 2.7.0-alpha.5
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skbkontur/markdown",
|
|
3
|
-
"version": "2.7.0-alpha.
|
|
3
|
+
"version": "2.7.0-alpha.5",
|
|
4
4
|
"packageManager": "yarn@4.13.0",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"@storybook/react": "8.2.8",
|
|
68
68
|
"@storybook/react-vite": "8.2.8",
|
|
69
69
|
"@storybook/test-runner": "0.19.1",
|
|
70
|
-
"@swc/core": "1.
|
|
70
|
+
"@swc/core": "1.15.11",
|
|
71
71
|
"@swc/jest": "0.2.26",
|
|
72
72
|
"@swc/plugin-styled-components": "5.0.0",
|
|
73
73
|
"@types/jest": "29.5.0",
|
|
@@ -121,4 +121,4 @@
|
|
|
121
121
|
"resolutions": {
|
|
122
122
|
"@types/react": "18.0.28"
|
|
123
123
|
}
|
|
124
|
-
}
|
|
124
|
+
}
|
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import { onInsertText } from '../utils/onInsertText';
|
|
2
|
+
/**
|
|
3
|
+
* RegExp для парсинга markdown-like list item.
|
|
4
|
+
*
|
|
5
|
+
* Поддерживает:
|
|
6
|
+
* - unordered lists: "- item", "* item", "+ item";
|
|
7
|
+
* - ordered lists: "1. item", "1) item";
|
|
8
|
+
* - checkbox items: "- [ ] item", "- [x] item done", "- [X] item done".
|
|
9
|
+
*
|
|
10
|
+
* Группы:
|
|
11
|
+
* - spacesBeforeMarker: отступы перед маркером списка;
|
|
12
|
+
* - orderedListNumber: номер ordered-list элемента;
|
|
13
|
+
* - orderedListDelimiter: разделитель ordered-list: "." или ")";
|
|
14
|
+
* - unorderedListMarker: маркер unordered-list: "*", "-" или "+";
|
|
15
|
+
* - checkboxListMarker: checkbox-маркер: "[ ]", "[x]" или "[X]";
|
|
16
|
+
* - text: текст элемента списка, может быть пустым.
|
|
17
|
+
*/
|
|
2
18
|
const listItemRegExp = /^(?<spacesBeforeMarker> *)(?:(?<orderedListNumber>\d+)(?<orderedListDelimiter>[.)])|(?<unorderedListMarker>[*+-])(?: +(?<checkboxListMarker>\[[ xX]]))?)(?: +(?<text>.*)|$)$/;
|
|
3
19
|
export function handleMarkdownListEnter(event, textareaNode) {
|
|
4
20
|
const { selectionStart, selectionEnd, value } = textareaNode;
|
|
@@ -20,6 +36,7 @@ export function handleMarkdownListEnter(event, textareaNode) {
|
|
|
20
36
|
const { spacesBeforeMarker, orderedListNumber, orderedListDelimiter, unorderedListMarker, checkboxListMarker, text, } = listLineMatch.groups;
|
|
21
37
|
event.stopPropagation();
|
|
22
38
|
event.preventDefault();
|
|
39
|
+
/* Если пункт списка пустой, удаляем маркер и завершаем список */
|
|
23
40
|
if (!text?.trim()) {
|
|
24
41
|
textareaNode.setSelectionRange(currentLineStartIndex, currentLineEndIndex);
|
|
25
42
|
return document.execCommand('delete');
|
|
@@ -27,11 +44,11 @@ export function handleMarkdownListEnter(event, textareaNode) {
|
|
|
27
44
|
if (orderedListNumber && orderedListDelimiter) {
|
|
28
45
|
const nextOrderedListNumber = Number(orderedListNumber) + 1;
|
|
29
46
|
const newLine = `\n${spacesBeforeMarker}${nextOrderedListNumber}${orderedListDelimiter} `;
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
47
|
+
const { isChanged, listEndIndex, renumberedList } = getRenumberedListLines(value, currentLineEndIndex, spacesBeforeMarker, orderedListDelimiter, nextOrderedListNumber + 1);
|
|
48
|
+
if (isChanged) {
|
|
32
49
|
const currentLineTextAfterCursor = value.slice(selectionStart, currentLineEndIndex);
|
|
33
|
-
textareaNode.setSelectionRange(selectionStart,
|
|
34
|
-
onInsertText(`${newLine}${currentLineTextAfterCursor}${
|
|
50
|
+
textareaNode.setSelectionRange(selectionStart, listEndIndex);
|
|
51
|
+
onInsertText(`${newLine}${currentLineTextAfterCursor}${renumberedList}`);
|
|
35
52
|
const cursorPosition = selectionStart + newLine.length;
|
|
36
53
|
return textareaNode.setSelectionRange(cursorPosition, cursorPosition);
|
|
37
54
|
}
|
|
@@ -54,18 +71,32 @@ function getCurrentLineEndIndex(text, cursorPosition) {
|
|
|
54
71
|
return text.length;
|
|
55
72
|
return currentLineEndIndex;
|
|
56
73
|
}
|
|
57
|
-
function
|
|
58
|
-
|
|
74
|
+
function getRenumberedListLines(value, currentLineEndIndex, spacesBeforeMarker, orderedListDelimiter, nextOrderedListNumber) {
|
|
75
|
+
let nextLineStartIndex = currentLineEndIndex + 1;
|
|
59
76
|
let currentOrderedListNumber = nextOrderedListNumber;
|
|
60
|
-
|
|
61
|
-
|
|
77
|
+
let isChanged = false;
|
|
78
|
+
let renumberedList = '';
|
|
79
|
+
let listEndIndex = currentLineEndIndex;
|
|
80
|
+
while (nextLineStartIndex < value.length) {
|
|
81
|
+
const nextLineEndIndex = getCurrentLineEndIndex(value, nextLineStartIndex);
|
|
82
|
+
const line = value.slice(nextLineStartIndex, nextLineEndIndex);
|
|
83
|
+
const listLineMatch = line.match(listItemRegExp);
|
|
62
84
|
const groups = listLineMatch?.groups;
|
|
63
85
|
if (!groups?.orderedListNumber ||
|
|
64
86
|
groups.spacesBeforeMarker !== spacesBeforeMarker ||
|
|
65
87
|
groups.orderedListDelimiter !== orderedListDelimiter)
|
|
66
88
|
break;
|
|
67
|
-
|
|
89
|
+
const renumberedLine = line.replace(`${spacesBeforeMarker}${groups.orderedListNumber}${orderedListDelimiter}`, `${spacesBeforeMarker}${currentOrderedListNumber}${orderedListDelimiter}`);
|
|
90
|
+
if (renumberedLine !== line)
|
|
91
|
+
isChanged = true;
|
|
92
|
+
renumberedList += `\n${renumberedLine}`;
|
|
93
|
+
listEndIndex = nextLineEndIndex;
|
|
94
|
+
nextLineStartIndex = nextLineEndIndex + 1;
|
|
68
95
|
currentOrderedListNumber += 1;
|
|
69
96
|
}
|
|
70
|
-
return
|
|
97
|
+
return {
|
|
98
|
+
isChanged,
|
|
99
|
+
listEndIndex,
|
|
100
|
+
renumberedList,
|
|
101
|
+
};
|
|
71
102
|
}
|