Skip to content

Commit 79c85ee

Browse files
committed
filter expander
1 parent f0304b2 commit 79c85ee

File tree

3 files changed

+235
-39
lines changed

3 files changed

+235
-39
lines changed

www/compile.js

Lines changed: 116 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,14 @@ function processLanguagePosts(langConfig) {
116116
if (!ALLOWED_EXTENSIONS.markdown.includes(ext)) return;
117117

118118
const rawContent = fs.readFileSync(path.join(monthPath, file), 'utf8');
119-
const content = mdToHtml(removeMetaTags(rawContent));
119+
const cleanContent = removeMetaTags(rawContent);
120+
const content = mdToHtml(cleanContent);
120121
const date = getPostDate(file, year, monthNum);
121122

122123
posts.push({
123124
title: file.match(/^\d{4}-\d{2}-\d{2}-\d{2}-\d{2}/) ? formatPostTitle(file) : file.replace('.md', ''),
124125
content,
126+
rawContent: cleanContent,
125127
date,
126128
month: monthNum
127129
});
@@ -241,10 +243,16 @@ function createCommentsLink(post, language, path) {
241243
let createPageWithMenu;
242244

243245
function createPostPage(post, language) {
246+
// Создаем копию поста с обработанным контентом для одиночной страницы
247+
const singlePost = {
248+
...post,
249+
content: mdToHtml(removeMetaTags(post.rawContent || ''), true) // Передаем true для isSinglePost
250+
};
251+
244252
return createPageWithMenu(
245253
post.title,
246254
`<div class="post" data-title="${post.title.date}" data-time="${post.title.time}">
247-
${post.content}
255+
${singlePost.content}
248256
${createCommentsSection(post, language)}
249257
</div>`,
250258
language==='uk'?'ukr':'index',
@@ -255,13 +263,20 @@ function createPostPage(post, language) {
255263
function createFilterInput(language) {
256264
const lang = language === 'uk' ? 'uk' : 'en';
257265
const placeholder = lang === 'uk' ? 'Фільтр:' : 'Filter:';
266+
const githubBtnText = lang === 'uk' ? 'У всіх постах (GitHub)' : 'In all posts (GitHub)';
258267

259268
return `
260269
<div class="filter-wrapper">
261270
<div class="input-container">
262271
<input type="text" id="postsFilter" class="posts-filter" placeholder="${placeholder}" autocomplete="off">
263272
<button type="button" id="clearFilter" class="clear-filter" aria-label="Очистить">✕</button>
264273
</div>
274+
<button type="button" id="githubSearchBtn" class="github-search-btn" aria-label="Search in GitHub">
275+
<svg viewBox="0 0 16 16">
276+
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
277+
</svg>
278+
${githubBtnText}
279+
</button>
265280
<div id="tagSuggestionsWrapper" class="tags-suggestions">
266281
<div id="tagSuggestions" class="tag-chips"></div>
267282
</div>
@@ -270,11 +285,79 @@ function createFilterInput(language) {
270285
document.addEventListener('DOMContentLoaded', () => {
271286
const filterInput = document.getElementById('postsFilter');
272287
const clearFilterBtn = document.getElementById('clearFilter');
288+
const githubSearchBtn = document.getElementById('githubSearchBtn');
273289
const posts = document.querySelectorAll('.post-wrapper');
274290
const tagSuggestions = document.getElementById('tagSuggestions');
275291
const tagSuggestionsWrapper = document.getElementById('tagSuggestionsWrapper');
276292
const minChars = 3;
277293
294+
// Управление расширением поля фильтра
295+
filterInput.addEventListener('focus', () => {
296+
filterInput.classList.add('expanded');
297+
// Показываем кнопку GitHub поиска при фокусе, если есть текст
298+
if (filterInput.value.trim().length > 0) {
299+
githubSearchBtn.classList.add('visible');
300+
}
301+
// Показываем кнопку очистки, если есть текст
302+
toggleClearButton();
303+
});
304+
305+
// Обновленная логика сворачивания поля
306+
filterInput.addEventListener('blur', () => {
307+
// Сужаем поле только если оно пустое
308+
if (filterInput.value.trim() === '') {
309+
filterInput.classList.remove('expanded');
310+
githubSearchBtn.classList.remove('visible');
311+
} else {
312+
// Если есть текст, оставляем поле развернутым
313+
filterInput.classList.add('expanded');
314+
}
315+
});
316+
317+
// Обработчик для кнопки поиска в GitHub
318+
githubSearchBtn.addEventListener('click', () => {
319+
const searchText = filterInput.value.trim();
320+
if (searchText.length > 0) {
321+
const encodedQuery = encodeURIComponent("repo:danvoronov/CodeWithLLM-Updates " + searchText);
322+
window.open("https://github.com/search?q=" + encodedQuery + " language%3AMarkdown&type=code", '_blank');
323+
}
324+
});
325+
326+
// Обновляем состояние поля и кнопок при вводе
327+
filterInput.addEventListener('input', () => {
328+
const hasText = filterInput.value.trim().length > 0;
329+
330+
// Управляем видимостью кнопки GitHub и состоянием поля
331+
if (hasText) {
332+
githubSearchBtn.classList.add('visible');
333+
filterInput.classList.add('expanded');
334+
} else {
335+
githubSearchBtn.classList.remove('visible');
336+
}
337+
338+
// Обновляем поиск и кнопку очистки
339+
updateSearch();
340+
toggleClearButton();
341+
});
342+
343+
// Добавляем обработчик клика по дате в вертикальной полоске
344+
document.querySelectorAll('.post').forEach(post => {
345+
// Используем делегирование событий для ::after
346+
post.addEventListener('click', (e) => {
347+
// Проверяем, был ли клик по ::after элементу
348+
// Для этого проверяем координаты клика относительно поста
349+
const rect = post.getBoundingClientRect();
350+
const isClickOnAfter = e.clientX <= rect.left + 30; // 30px - примерная ширина ::after
351+
352+
if (isClickOnAfter) {
353+
const postUrl = post.getAttribute('data-post-url');
354+
if (postUrl) {
355+
window.location.href = postUrl;
356+
}
357+
}
358+
});
359+
});
360+
278361
// Запоминаем положение скролла перед фильтрацией
279362
let lastScrollPosition = 0;
280363
let lastHighlightedPosts = [];
@@ -296,13 +379,18 @@ function createFilterInput(language) {
296379
clearFilterBtn.addEventListener('click', () => {
297380
filterInput.value = '';
298381
updateSearch();
299-
filterInput.focus();
382+
filterInput.focus(); // Сохраняем фокус на поле
300383
toggleClearButton();
384+
githubSearchBtn.classList.remove('visible'); // Скрываем кнопку GitHub
385+
386+
// Не сужаем поле, так как оно в фокусе
301387
});
302388
303389
// Функция для отображения/скрытия кнопки очистки
304390
function toggleClearButton() {
305-
clearFilterBtn.style.display = filterInput.value.length > 0 ? 'block' : 'none';
391+
// Всегда показываем кнопку очистки, если в поле есть текст
392+
const hasText = filterInput.value.trim().length > 0;
393+
clearFilterBtn.style.display = hasText ? 'block' : 'none';
306394
}
307395
308396
// Инициализация состояния кнопки очистки
@@ -480,12 +568,22 @@ function createFilterInput(language) {
480568
481569
// Очистка фильтра при нажатии Escape
482570
document.addEventListener('keydown', function(e) {
483-
if (e.key === 'Escape' && document.activeElement === filterInput) {
484-
filterInput.value = '';
485-
updateSearch();
486-
filterInput.blur();
487-
tagSuggestionsWrapper.classList.remove('visible');
488-
toggleClearButton();
571+
if (e.key === 'Escape') {
572+
// Проверяем, есть ли текст в поле фильтра
573+
if (filterInput.value.trim() !== '') {
574+
filterInput.classList.remove('expanded');
575+
filterInput.value = '';
576+
updateSearch();
577+
toggleClearButton();
578+
githubSearchBtn.classList.remove('visible'); // Скрываем кнопку GitHub
579+
tagSuggestionsWrapper.classList.remove('visible');
580+
581+
// Если фокус был в поле ввода, убираем его
582+
if (document.activeElement === filterInput) {
583+
filterInput.blur();
584+
// Поле сузится автоматически в обработчике blur, так как оно пустое
585+
}
586+
}
489587
}
490588
});
491589
});
@@ -508,7 +606,7 @@ function createMonthArchivePage(posts, month, year, language, monthsData, curren
508606
const fullPath = `${basePath}${monthKey}/${postSlug}/`;
509607
return `
510608
<div class="post-wrapper">
511-
<div class="post" data-title="${post.title.date}" data-time="${post.title.time}" data-date="${post.date}">
609+
<div class="post" data-title="${post.title.date}" data-time="${post.title.time}" data-date="${post.date}" data-post-url="${fullPath}">
512610
${post.content}
513611
${createCommentsLink(post, language, fullPath)}
514612
</div>
@@ -624,14 +722,15 @@ function createBlogContent(posts, language) {
624722
const postSlug = getPostSlug(post);
625723
const date = new Date(post.date);
626724
const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
627-
const path = language === 'uk' ?
628-
`${siteUrl}ua/${monthKey}/${postSlug}/` :
629-
`${siteUrl}${monthKey}/${postSlug}/`;
725+
const relativePath = language === 'uk' ?
726+
`/ua/${monthKey}/${postSlug}/` :
727+
`/${monthKey}/${postSlug}/`;
728+
const fullPath = `${siteUrl}${relativePath.substring(1)}`;
630729
return `
631730
<div class="post-wrapper">
632-
<div class="post" data-title="${post.title.date}" data-time="${post.title.time}" data-date="${post.date}">
731+
<div class="post" data-title="${post.title.date}" data-time="${post.title.time}" data-date="${post.date}" data-post-url="${fullPath}">
633732
${post.content}
634-
${createCommentsLink(post, language, path)}
733+
${createCommentsLink(post, language, relativePath)}
635734
</div>
636735
</div>
637736
`;
@@ -756,16 +855,6 @@ function generateMenu(activeMenu, posts = [], currentMonth = '') {
756855
body.classList.toggle('menu-open');
757856
});
758857
});
759-
760-
document.addEventListener('keydown', function(e) {
761-
if (e.key === 'Escape') {
762-
const filterInput = document.getElementById('postsFilter');
763-
if (filterInput) {
764-
filterInput.value = '';
765-
filterInput.dispatchEvent(new Event('input'));
766-
}
767-
}
768-
});
769858
</script>`;
770859
}
771860

@@ -820,7 +909,7 @@ function createPage(title, content, activeMenu, posts = [], currentMonth = '', p
820909
<body>
821910
<div class="wrapper">
822911
${preGeneratedMenu || generateMenu(activeMenu, posts, currentMonth)}
823-
${(activeMenu === 'index' || activeMenu === 'ukr') ? createFilterInput(activeMenu === 'ukr' ? 'uk' : 'en') : ''}
912+
${(activeMenu === 'index' || activeMenu === 'ukr') && posts.length > 1 ? createFilterInput(activeMenu === 'ukr' ? 'uk' : 'en') : ''}
824913
${content}
825914
</div>
826915
</body>

www/src/md2html.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,15 @@ marked.use({
5252
}
5353
});
5454

55-
function mdToHtml(content) {
55+
function mdToHtml(content, isSinglePost = false) {
5656
// Заменяем теги на красивые спаны перед обработкой markdown
57-
content = content.replace(/(?<=\s)#([a-zA-Zа-яА-ЯёЁіІїЇєЄ]+)/g, '<span class="post-tag" onclick="document.getElementById(\'postsFilter\').value=\'#$1\';document.getElementById(\'postsFilter\').dispatchEvent(new Event(\'input\'))">#$1</span>');
57+
if (isSinglePost) {
58+
// Для страницы отдельного поста не добавляем обработчик клика
59+
content = content.replace(/(?<=\s)#([a-zA-Zа-яА-ЯёЁіІїЇєЄ]+)/g, '<span class="post-tag">#$1</span>');
60+
} else {
61+
// Для страниц со списком постов добавляем обработчик клика
62+
content = content.replace(/(?<=\s)#([a-zA-Zа-яА-ЯёЁіІїЇєЄ]+)/g, '<span class="post-tag" onclick="document.getElementById(\'postsFilter\').value=\'#$1\';document.getElementById(\'postsFilter\').dispatchEvent(new Event(\'input\'))">#$1</span>');
63+
}
5864

5965
return marked.parse(content.replace(/<!--[\s\S]*?-->/g, ''));
6066
}

0 commit comments

Comments
 (0)