Skip to content

Commit 40b0ed9

Browse files
authored
Merge pull request #2 from DevLabStudy/copilot/update-faq-accordion-elements
Automatyczna transformacja sekcji FAQ w newsach do dostępnego accordion + odfiltrowanie pytań z TOC
2 parents 1016cbb + da10f9a commit 40b0ed9

1 file changed

Lines changed: 151 additions & 1 deletion

File tree

‎_layouts/news.html‎

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,74 @@
130130
margin-top: 10px;
131131
}
132132

133+
/* FAQ ACCORDION W TREŚCI NEWSA */
134+
.faq-accordion {
135+
margin: 20px 0 35px 0;
136+
border: 1px solid var(--border);
137+
border-radius: 10px;
138+
background: rgba(255, 255, 255, 0.02);
139+
overflow: hidden;
140+
}
141+
.faq-accordion-intro {
142+
padding: 18px 20px 4px 20px;
143+
}
144+
.faq-item {
145+
border-top: 1px solid var(--border);
146+
}
147+
.faq-item:first-of-type {
148+
border-top: 0;
149+
}
150+
.faq-question {
151+
list-style: none;
152+
cursor: pointer;
153+
padding: 16px 20px;
154+
font-family: 'JetBrains Mono', monospace;
155+
font-weight: 700;
156+
color: #fff;
157+
display: flex;
158+
align-items: center;
159+
justify-content: space-between;
160+
gap: 15px;
161+
transition: background 0.2s ease, color 0.2s ease;
162+
outline: none;
163+
}
164+
.faq-question::-webkit-details-marker {
165+
display: none;
166+
}
167+
.faq-question::after {
168+
content: '⌄';
169+
font-size: 1.05rem;
170+
color: var(--accent-blue);
171+
transition: transform 0.2s ease;
172+
flex-shrink: 0;
173+
}
174+
.faq-item[open] .faq-question::after {
175+
transform: rotate(180deg);
176+
}
177+
.faq-question:hover {
178+
background: rgba(0, 210, 255, 0.08);
179+
color: var(--accent-blue);
180+
}
181+
.faq-question:focus-visible {
182+
box-shadow: inset 0 0 0 2px var(--accent-blue);
183+
border-radius: 4px;
184+
}
185+
.faq-answer {
186+
max-height: 0;
187+
overflow: hidden;
188+
opacity: 0;
189+
transition: max-height 0.28s ease, opacity 0.2s ease;
190+
padding: 0 20px;
191+
}
192+
.faq-item[open] .faq-answer {
193+
max-height: 1200px;
194+
opacity: 1;
195+
padding: 0 20px 18px 20px;
196+
}
197+
.faq-answer > *:last-child {
198+
margin-bottom: 0;
199+
}
200+
133201
/* SPIS TREŚCI (TOC) W BOCZNYM PANELU */
134202
.sidebar-toc {
135203
position: sticky;
@@ -287,6 +355,9 @@
287355
.post-container { grid-template-columns: 1fr; padding: 0 20px; }
288356
.sidebar-toc { position: static; }
289357
.back-to-news-floating { bottom: 20px; right: 20px; padding: 10px 14px; font-size: 0.75rem; }
358+
.faq-question { padding: 14px 16px; font-size: 0.9rem; }
359+
.faq-item[open] .faq-answer { padding: 0 16px 16px 16px; }
360+
.faq-answer { padding: 0 16px; }
290361
}
291362
</style>
292363

@@ -340,10 +411,89 @@ <h4 class="toc-title">// Spis Treści</h4>
340411
</a>
341412

342413
<script>
414+
// FAQ markdown structure expected in the post body:
415+
// - `## FAQ` / `## Najczęściej zadawane pytania` (or heading containing these phrases) starts FAQ section
416+
// - each `###` in this section is treated as one question
417+
// - all elements under that `###` until next `###` or next `##` are treated as answer content
418+
function normalizeHeadingText(text) {
419+
return (text || '')
420+
.toLowerCase()
421+
.normalize('NFD')
422+
.replace(/[\u0300-\u036f]/g, '')
423+
.trim();
424+
}
425+
426+
function isFaqSectionHeading(text) {
427+
const normalized = normalizeHeadingText(text);
428+
return normalized.includes('faq') || normalized.includes('najczesciej zadawane pytania');
429+
}
430+
431+
function transformFaqToAccordion(articleBody) {
432+
const headings = Array.from(articleBody.querySelectorAll('h2'));
433+
const faqHeading = headings.find((heading) => isFaqSectionHeading(heading.textContent));
434+
if (!faqHeading) return;
435+
436+
const sectionNodes = [];
437+
let cursor = faqHeading.nextElementSibling;
438+
while (cursor) {
439+
if (cursor.tagName === 'H1' || cursor.tagName === 'H2') break;
440+
sectionNodes.push(cursor);
441+
cursor = cursor.nextElementSibling;
442+
}
443+
444+
if (sectionNodes.length === 0) return;
445+
if (!sectionNodes.some((node) => node.tagName === 'H3')) return;
446+
447+
const faqWrapper = document.createElement('section');
448+
faqWrapper.className = 'faq-accordion';
449+
faqWrapper.setAttribute('aria-label', 'Najczęściej zadawane pytania');
450+
451+
const faqIntro = document.createElement('div');
452+
faqIntro.className = 'faq-accordion-intro';
453+
454+
let currentAnswer = null;
455+
let hasQuestions = false;
456+
457+
sectionNodes.forEach((node) => {
458+
if (node.tagName === 'H3') {
459+
const details = document.createElement('details');
460+
details.className = 'faq-item';
461+
462+
const summary = document.createElement('summary');
463+
summary.className = 'faq-question';
464+
summary.textContent = node.textContent.trim();
465+
details.appendChild(summary);
466+
467+
currentAnswer = document.createElement('div');
468+
currentAnswer.className = 'faq-answer';
469+
details.appendChild(currentAnswer);
470+
471+
faqWrapper.appendChild(details);
472+
node.remove();
473+
hasQuestions = true;
474+
return;
475+
}
476+
477+
if (currentAnswer) {
478+
currentAnswer.appendChild(node);
479+
} else {
480+
faqIntro.appendChild(node);
481+
}
482+
});
483+
484+
if (!hasQuestions) return;
485+
if (faqIntro.childNodes.length > 0) faqWrapper.prepend(faqIntro);
486+
faqHeading.insertAdjacentElement('afterend', faqWrapper);
487+
}
488+
343489
// AUTOMATYCZNY SPIS TREŚCI ZE STRUKTURY NAGŁÓWKÓW
344490
const articleBody = document.getElementById('articleBody');
345491
const tocList = document.getElementById('tocList');
346-
const headings = articleBody.querySelectorAll('h2, h3');
492+
transformFaqToAccordion(articleBody);
493+
494+
const headings = Array.from(articleBody.querySelectorAll('h2, h3')).filter(
495+
(heading) => !heading.closest('.faq-accordion')
496+
);
347497

348498
if(headings.length === 0) {
349499
tocList.innerHTML = '<li style="color: var(--text-muted)">// Brak nagłówków</li>';

0 commit comments

Comments
 (0)