diff --git a/clickable_tags_final_screenshot.png b/clickable_tags_final_screenshot.png
new file mode 100644
index 0000000..9e65dcf
Binary files /dev/null and b/clickable_tags_final_screenshot.png differ
diff --git a/public/app.js b/public/app.js
index 826a1e2..2cd5180 100644
--- a/public/app.js
+++ b/public/app.js
@@ -15,6 +15,7 @@ const linkBtn = document.getElementById("linkBtn");
// Глобальные переменные для заметок и фильтрации
let allNotes = [];
let selectedDateFilter = null;
+let selectedTagFilter = null;
// Функция для получения текущей даты и времени
function getFormattedDateTime() {
@@ -37,6 +38,90 @@ function autoExpandTextarea(textarea) {
textarea.style.height = textarea.scrollHeight + "px";
}
+// Функция для извлечения тегов из текста заметки
+function extractTags(content) {
+ const tagRegex = /#(\w+)/g;
+ const tags = [];
+ let match;
+
+ while ((match = tagRegex.exec(content)) !== null) {
+ const tag = match[1].toLowerCase();
+ if (!tags.includes(tag)) {
+ tags.push(tag);
+ }
+ }
+
+ return tags;
+}
+
+// Функция для преобразования тегов в заметках в кликабельные элементы
+function makeTagsClickable(content) {
+ const tagRegex = /#(\w+)/g;
+ return content.replace(
+ tagRegex,
+ '#$1'
+ );
+}
+
+// Функция для получения всех уникальных тегов из заметок
+function getAllTags(notes) {
+ const tagCounts = {};
+
+ notes.forEach((note) => {
+ const tags = extractTags(note.content);
+ tags.forEach((tag) => {
+ tagCounts[tag] = (tagCounts[tag] || 0) + 1;
+ });
+ });
+
+ return tagCounts;
+}
+
+// Функция для отображения тегов
+function renderTags() {
+ const tagsContainer = document.getElementById("tagsContainer");
+ if (!tagsContainer) return;
+
+ const tagCounts = getAllTags(allNotes);
+ const sortedTags = Object.keys(tagCounts).sort();
+
+ if (sortedTags.length === 0) {
+ tagsContainer.innerHTML =
+ '
Нет тегов
';
+ return;
+ }
+
+ tagsContainer.innerHTML = sortedTags
+ .map((tag) => {
+ const count = tagCounts[tag];
+ const isActive = selectedTagFilter === tag ? "active" : "";
+ return `#${tag}${count}`;
+ })
+ .join("");
+
+ // Добавляем обработчики кликов для тегов
+ tagsContainer.querySelectorAll(".tag").forEach((tagElement) => {
+ tagElement.addEventListener("click", handleTagClick);
+ });
+}
+
+// Обработчик клика на тег
+function handleTagClick(event) {
+ const clickedTag = event.target.closest(".tag").dataset.tag;
+
+ // Если кликнули на тот же тег, снимаем фильтр
+ if (selectedTagFilter === clickedTag) {
+ selectedTagFilter = null;
+ } else {
+ selectedTagFilter = clickedTag;
+ }
+
+ // Перерисовываем заметки и теги
+ renderNotes(allNotes);
+ renderTags();
+ updateFilterIndicator();
+}
+
// Привязываем авторасширение к текстовому полю для создания заметки
noteInput.addEventListener("input", function () {
autoExpandTextarea(noteInput);
@@ -201,6 +286,7 @@ async function loadNotes() {
allNotes = notes; // Сохраняем все заметки в глобальную переменную
renderNotes(notes);
renderCalendar(); // Обновляем календарь после загрузки заметок
+ renderTags(); // Обновляем теги после загрузки заметок
} catch (error) {
console.error("Ошибка:", error);
notesList.innerHTML = "
Ошибка загрузки заметок
";
@@ -211,25 +297,44 @@ async function loadNotes() {
function renderNotes(notes) {
notesList.innerHTML = "";
- // Фильтруем заметки, если выбрана дата
+ // Фильтруем заметки по дате и тегам
let notesToDisplay = notes;
+
if (selectedDateFilter) {
- notesToDisplay = notes.filter((note) => note.date === selectedDateFilter);
+ notesToDisplay = notesToDisplay.filter(
+ (note) => note.date === selectedDateFilter
+ );
+ }
+
+ if (selectedTagFilter) {
+ notesToDisplay = notesToDisplay.filter((note) => {
+ const tags = extractTags(note.content);
+ return tags.includes(selectedTagFilter);
+ });
}
// Если нет заметок для отображения
if (notesToDisplay.length === 0) {
- if (selectedDateFilter) {
- notesList.innerHTML = `
Нет заметок за выбранную дату (${selectedDateFilter})
`;
- } else {
- notesList.innerHTML =
- '
Заметок пока нет
';
+ let message = "Заметок пока нет";
+
+ if (selectedDateFilter && selectedTagFilter) {
+ message = `Нет заметок за ${selectedDateFilter} с тегом #${selectedTagFilter}`;
+ } else if (selectedDateFilter) {
+ message = `Нет заметок за выбранную дату (${selectedDateFilter})`;
+ } else if (selectedTagFilter) {
+ message = `Нет заметок с тегом #${selectedTagFilter}`;
}
+
+ notesList.innerHTML = `
${message}
`;
return;
}
// Итерируемся по заметкам в обычном порядке, чтобы новые были сверху
notesToDisplay.forEach(function (note) {
+ // Преобразуем теги в кликабельные элементы перед парсингом markdown
+ const contentWithClickableTags = makeTagsClickable(note.content);
+ const parsedContent = marked.parse(contentWithClickableTags);
+
const noteHtml = `
@@ -244,7 +349,7 @@ function renderNotes(notes) {
${marked.parse(note.content)}
+ )}">${parsedContent}
`;
notesList.insertAdjacentHTML("afterbegin", noteHtml);
@@ -252,6 +357,9 @@ function renderNotes(notes) {
// Добавляем обработчики событий для кнопок редактирования и удаления
addNoteEventListeners();
+
+ // Добавляем обработчики кликов для тегов в заметках
+ addTagClickListeners();
}
// Функция для добавления обработчиков событий к заметкам
@@ -375,6 +483,30 @@ function addNoteEventListeners() {
});
}
+// Функция для добавления обработчиков кликов на теги в заметках
+function addTagClickListeners() {
+ document.querySelectorAll(".textNote .tag-in-note").forEach((tagElement) => {
+ tagElement.addEventListener("click", function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ const clickedTag = this.dataset.tag.toLowerCase();
+
+ // Если кликнули на тот же тег, снимаем фильтр
+ if (selectedTagFilter === clickedTag) {
+ selectedTagFilter = null;
+ } else {
+ selectedTagFilter = clickedTag;
+ }
+
+ // Перерисовываем заметки и теги
+ renderNotes(allNotes);
+ renderTags();
+ updateFilterIndicator();
+ });
+ });
+}
+
// Функция сохранения заметки (вынесена отдельно для повторного использования)
async function saveNote() {
if (noteInput.value.trim() !== "") {
@@ -630,9 +762,10 @@ function handleDayClick(event) {
selectedDateFilter = clickedDate;
}
- // Перерисовываем заметки и календарь
+ // Перерисовываем заметки, календарь и теги
renderNotes(allNotes);
renderCalendar();
+ renderTags();
updateFilterIndicator();
}
@@ -641,9 +774,21 @@ function updateFilterIndicator() {
const filterIndicator = document.getElementById("filter-indicator");
if (!filterIndicator) return;
+ const filters = [];
+
if (selectedDateFilter) {
+ filters.push(`Дата: ${selectedDateFilter}`);
+ }
+
+ if (selectedTagFilter) {
+ filters.push(`Тег: #${selectedTagFilter}`);
+ }
+
+ if (filters.length > 0) {
filterIndicator.style.display = "inline-block";
- filterIndicator.innerHTML = `Фильтр: ${selectedDateFilter} `;
+ filterIndicator.innerHTML = `Фильтр: ${filters.join(
+ ", "
+ )} `;
// Добавляем обработчик клика для кнопки сброса
const clearBtn = document.getElementById("clear-filter-btn");
@@ -658,8 +803,10 @@ function updateFilterIndicator() {
// Функция для сброса фильтра (глобальная)
window.clearFilter = function () {
selectedDateFilter = null;
+ selectedTagFilter = null;
renderNotes(allNotes);
renderCalendar();
+ renderTags();
updateFilterIndicator();
};
diff --git a/public/notes.html b/public/notes.html
index 6f08bcd..b1a4c8c 100644
--- a/public/notes.html
+++ b/public/notes.html
@@ -29,6 +29,16 @@
+
+
+