From e3b98ea8d3dccf1158e17356f1cefbb620bd030d Mon Sep 17 00:00:00 2001 From: Fovway Date: Tue, 4 Nov 2025 12:39:14 +0700 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D1=87=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B2=D1=8B=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=BA=D0=BE=D0=B2=20=D0=B8=20=D1=86=D0=B8=D1=82?= =?UTF-8?q?=D0=B0=D1=82=20=D0=B2=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B0=D1=85=20NoteEditor=20=D0=B8=20NoteItem?= =?UTF-8?q?.=20=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B0=20=D0=BB?= =?UTF-8?q?=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=82=D0=B5=D0=B3=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=B2=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=B8=20=D0=BE=D1=82=20=D1=84=D0=BE=D1=80=D0=BC?= =?UTF-8?q?=D0=B0=D1=82=D0=B0=20=D0=B2=D1=8B=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=82=D0=B5=D0=BA=D1=81=D1=82=D0=B0?= =?UTF-8?q?,=20=D0=B2=D0=BA=D0=BB=D1=8E=D1=87=D0=B0=D1=8F=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA=D1=83=20=D1=87=D0=B5=D0=BA?= =?UTF-8?q?=D0=B1=D0=BE=D0=BA=D1=81=D0=BE=D0=B2=20=D0=B8=20=D0=BD=D1=83?= =?UTF-8?q?=D0=BC=D0=B5=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20?= =?UTF-8?q?=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=BE=D0=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/notes/NoteEditor.tsx | 82 +++++++++++++++++++++++++---- src/components/notes/NoteItem.tsx | 82 +++++++++++++++++++++++++---- 2 files changed, 146 insertions(+), 18 deletions(-) diff --git a/src/components/notes/NoteEditor.tsx b/src/components/notes/NoteEditor.tsx index 3d6f6ab..2fc031a 100644 --- a/src/components/notes/NoteEditor.tsx +++ b/src/components/notes/NoteEditor.tsx @@ -327,15 +327,79 @@ export const NoteEditor: React.FC = ({ onSave }) => { newEnd = start + innerText.length; } } else { - // Добавляем теги - newText = - content.substring(0, start) + - before + - selectedText + - after + - content.substring(end); - newStart = start + before.length; - newEnd = end + before.length; + // Проверяем, является ли это форматированием списка или цитаты + const isListFormatting = /^[-*+]\s|^\d+\.\s|^- \[ \]\s|^>\s/.test(before); + const isMultiline = selectedText.includes("\n"); + + if (isListFormatting && isMultiline) { + // Обрабатываем многострочное выделение для списков + const lines = selectedText.split("\n"); + let processedLines: string[] = []; + let currentNumber = 1; + let isFirstNonEmptyLine = true; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const trimmedLine = line.trim(); + + // Пропускаем пустые строки + if (trimmedLine === "") { + processedLines.push(line); + continue; + } + + // Определяем отступ текущей строки + const indentMatch = line.match(/^(\s*)/); + const indent = indentMatch ? indentMatch[1] : ""; + + // Обрабатываем в зависимости от типа маркера + if (before.startsWith("- [ ]")) { + // Чекбокс + processedLines.push(indent + "- [ ] " + trimmedLine); + } else if (before.startsWith("- ")) { + // Маркированный список + processedLines.push(indent + "- " + trimmedLine); + } else if (before.match(/^\d+\.\s/)) { + // Нумерованный список - извлекаем начальный номер + const numberMatch = before.match(/^(\d+)\.\s/); + if (numberMatch && isFirstNonEmptyLine) { + // Для первой непустой строки используем номер из маркера + currentNumber = parseInt(numberMatch[1]); + isFirstNonEmptyLine = false; + } else if (isFirstNonEmptyLine) { + // Если маркера нет, начинаем с 1 + currentNumber = 1; + isFirstNonEmptyLine = false; + } + processedLines.push(indent + currentNumber + ". " + trimmedLine); + currentNumber++; + } else if (before.startsWith("> ")) { + // Цитата + processedLines.push(indent + "> " + trimmedLine); + } else { + // Для других форматов просто добавляем маркер + processedLines.push(indent + before + trimmedLine); + } + } + + const processedText = processedLines.join("\n"); + newText = + content.substring(0, start) + + processedText + + content.substring(end); + newStart = start + before.length; + newEnd = start + processedText.length; + } else { + // Добавляем теги обычным способом + newText = + content.substring(0, start) + + before + + selectedText + + after + + content.substring(end); + newStart = start + before.length; + newEnd = end + before.length; + } } setContent(newText); diff --git a/src/components/notes/NoteItem.tsx b/src/components/notes/NoteItem.tsx index 691f35e..4311ae7 100644 --- a/src/components/notes/NoteItem.tsx +++ b/src/components/notes/NoteItem.tsx @@ -416,15 +416,79 @@ export const NoteItem: React.FC = ({ newEnd = start + innerText.length; } } else { - // Добавляем теги - newText = - editContent.substring(0, start) + - before + - selectedText + - after + - editContent.substring(end); - newStart = start + before.length; - newEnd = end + before.length; + // Проверяем, является ли это форматированием списка или цитаты + const isListFormatting = /^[-*+]\s|^\d+\.\s|^- \[ \]\s|^>\s/.test(before); + const isMultiline = selectedText.includes("\n"); + + if (isListFormatting && isMultiline) { + // Обрабатываем многострочное выделение для списков + const lines = selectedText.split("\n"); + let processedLines: string[] = []; + let currentNumber = 1; + let isFirstNonEmptyLine = true; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const trimmedLine = line.trim(); + + // Пропускаем пустые строки + if (trimmedLine === "") { + processedLines.push(line); + continue; + } + + // Определяем отступ текущей строки + const indentMatch = line.match(/^(\s*)/); + const indent = indentMatch ? indentMatch[1] : ""; + + // Обрабатываем в зависимости от типа маркера + if (before.startsWith("- [ ]")) { + // Чекбокс + processedLines.push(indent + "- [ ] " + trimmedLine); + } else if (before.startsWith("- ")) { + // Маркированный список + processedLines.push(indent + "- " + trimmedLine); + } else if (before.match(/^\d+\.\s/)) { + // Нумерованный список - извлекаем начальный номер + const numberMatch = before.match(/^(\d+)\.\s/); + if (numberMatch && isFirstNonEmptyLine) { + // Для первой непустой строки используем номер из маркера + currentNumber = parseInt(numberMatch[1]); + isFirstNonEmptyLine = false; + } else if (isFirstNonEmptyLine) { + // Если маркера нет, начинаем с 1 + currentNumber = 1; + isFirstNonEmptyLine = false; + } + processedLines.push(indent + currentNumber + ". " + trimmedLine); + currentNumber++; + } else if (before.startsWith("> ")) { + // Цитата + processedLines.push(indent + "> " + trimmedLine); + } else { + // Для других форматов просто добавляем маркер + processedLines.push(indent + before + trimmedLine); + } + } + + const processedText = processedLines.join("\n"); + newText = + editContent.substring(0, start) + + processedText + + editContent.substring(end); + newStart = start + before.length; + newEnd = start + processedText.length; + } else { + // Добавляем теги обычным способом + newText = + editContent.substring(0, start) + + before + + selectedText + + after + + editContent.substring(end); + newStart = start + before.length; + newEnd = end + before.length; + } } setEditContent(newText);