mirror of
https://github.com/hugo-fixit/FixIt.git
synced 2026-08-24 15:28:57 +00:00
feat(buttons): replace role="button" with button elements for improved accessibility
This commit is contained in:
@@ -32,7 +32,12 @@ iframe {
|
||||
@include border-radius;
|
||||
}
|
||||
|
||||
[role='button'] {
|
||||
button {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
@@ -172,11 +172,11 @@ header {
|
||||
.search-toggle {
|
||||
left: 0.5rem;
|
||||
right: auto;
|
||||
@include focus-visible-ring;
|
||||
|
||||
#header-desktop.open & {
|
||||
left: 1rem;
|
||||
}
|
||||
@include focus-visible-ring;
|
||||
}
|
||||
|
||||
.search-loading {
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@mixin focus-visible-ring($size: 1.5em, $offset: 0.2rem) {
|
||||
@mixin focus-visible-ring($size: 1.3em, $offset: 0.2rem) {
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
|
||||
|
||||
@@ -14,15 +14,14 @@
|
||||
padding: 0.5rem 0.625rem;
|
||||
color: fixit-var(global-font-secondary-color);
|
||||
transition: color 0.4s ease;
|
||||
@include border-radius(full);
|
||||
background-color: light-dark(#f1f1f1, #1e1e1f);
|
||||
@include border-radius(full);
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
outline: none;
|
||||
color: fixit-var(global-font-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@include media('print') {
|
||||
|
||||
@@ -236,7 +236,7 @@ pre {
|
||||
padding: 0.4rem;
|
||||
}
|
||||
|
||||
[role='button'] {
|
||||
button {
|
||||
padding: 0.4rem;
|
||||
|
||||
@include media('print') {
|
||||
@@ -276,7 +276,7 @@ pre {
|
||||
|
||||
&.line-wrapping .line-wrap-btn,
|
||||
&:not(.line-nos-hidden) .line-nos-btn,
|
||||
.code-header [role='button']:hover,
|
||||
.code-header button:hover,
|
||||
&:has([contenteditable='true']) .edit-btn {
|
||||
color: fixit-var(global-link-hover-color);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
// Diagram copy button styles
|
||||
.diagram-copy-btn {
|
||||
box-sizing: content-box;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
position: absolute;
|
||||
|
||||
+234
-97
@@ -25,10 +25,12 @@ class FixIt {
|
||||
this.isDark = isDarkMode();
|
||||
this.newScrollTop = getScrollTop();
|
||||
this.oldScrollTop = this.newScrollTop;
|
||||
this.maskOverlays = new Map();
|
||||
this.activeMaskOverlay = null;
|
||||
this.activeTocId = null;
|
||||
this.scrollEventSet = new Set();
|
||||
this.resizeEventSet = new Set();
|
||||
this.switchThemeEventSet = new Set();
|
||||
this.clickMaskEventSet = new Set();
|
||||
this.beforeprintEventSet = new Set();
|
||||
this.afterprintEventSet = new Set();
|
||||
window.objectFitImages && objectFitImages();
|
||||
@@ -68,17 +70,54 @@ class FixIt {
|
||||
this.config.twemoji && twemoji.parse(target);
|
||||
}
|
||||
|
||||
initRoleButtons(target = document) {
|
||||
forEach(target.querySelectorAll('[role="button"]'), ($el) => {
|
||||
if ($el.dataset.init) return;
|
||||
$el.dataset.init = 'true';
|
||||
$el.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
$el.click();
|
||||
registerMaskOverlay(name, handlers) {
|
||||
this.maskOverlays.set(name, handlers);
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
|
||||
syncMaskState() {
|
||||
document.getElementById('mask')?.classList.toggle('blur', Boolean(this.activeMaskOverlay));
|
||||
}
|
||||
|
||||
openMaskOverlay(name) {
|
||||
this.disableScrollEvent = true;
|
||||
const overlay = this.maskOverlays.get(name);
|
||||
if (!overlay) return;
|
||||
if (this.activeMaskOverlay && this.activeMaskOverlay !== name) {
|
||||
this.closeMaskOverlay(this.activeMaskOverlay, true);
|
||||
}
|
||||
overlay.onOpen?.();
|
||||
this.activeMaskOverlay = name;
|
||||
this.syncMaskState();
|
||||
}
|
||||
|
||||
closeMaskOverlay(name, skipSync = false) {
|
||||
this.disableScrollEvent = false;
|
||||
const overlay = this.maskOverlays.get(name);
|
||||
if (!overlay) return;
|
||||
overlay.onClose?.();
|
||||
if (this.activeMaskOverlay === name) {
|
||||
this.activeMaskOverlay = null;
|
||||
}
|
||||
!skipSync && this.syncMaskState();
|
||||
}
|
||||
|
||||
toggleMaskOverlay(name) {
|
||||
const overlay = this.maskOverlays.get(name);
|
||||
if (!overlay) return;
|
||||
const isActive = overlay.isActive?.() ?? this.activeMaskOverlay === name;
|
||||
if (this.activeMaskOverlay === name && isActive) {
|
||||
this.closeMaskOverlay(name);
|
||||
return;
|
||||
}
|
||||
this.openMaskOverlay(name);
|
||||
}
|
||||
|
||||
closeActiveMaskOverlay() {
|
||||
if (!this.activeMaskOverlay) {
|
||||
this.syncMaskState();
|
||||
return;
|
||||
}
|
||||
this.closeMaskOverlay(this.activeMaskOverlay);
|
||||
}
|
||||
|
||||
initMenu() {
|
||||
@@ -95,19 +134,28 @@ class FixIt {
|
||||
initMenuMobile() {
|
||||
const $menuToggleMobile = document.getElementById('menu-toggle-mobile');
|
||||
const $menuMobile = document.getElementById('menu-mobile');
|
||||
const $mask = document.getElementById('mask');
|
||||
$menuToggleMobile.addEventListener('click', (event) => {
|
||||
$mask.classList.toggle('blur');
|
||||
$menuToggleMobile.classList.toggle('active');
|
||||
$menuMobile.classList.toggle('active');
|
||||
$menuToggleMobile.setAttribute('aria-expanded', $menuToggleMobile.classList.contains('active') ? 'true' : 'false');
|
||||
if (!$menuToggleMobile || !$menuMobile) return;
|
||||
this.registerMaskOverlay('menu-mobile', {
|
||||
isActive: () => $menuMobile.classList.contains('active'),
|
||||
onOpen: () => {
|
||||
$menuToggleMobile.classList.add('active');
|
||||
$menuMobile.classList.add('active');
|
||||
$menuToggleMobile.setAttribute('aria-expanded', 'true');
|
||||
},
|
||||
onClose: () => {
|
||||
$menuToggleMobile.classList.remove('active');
|
||||
$menuMobile.classList.remove('active');
|
||||
$menuToggleMobile.setAttribute('aria-expanded', 'false');
|
||||
},
|
||||
});
|
||||
$menuToggleMobile.addEventListener('click', () => {
|
||||
this.toggleMaskOverlay('menu-mobile');
|
||||
}, false);
|
||||
this._menuMobileOnClickMask = this._menuMobileOnClickMask || (() => {
|
||||
$menuToggleMobile.classList.remove('active');
|
||||
$menuMobile.classList.remove('active');
|
||||
$menuToggleMobile.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
this.clickMaskEventSet.add(this._menuMobileOnClickMask);
|
||||
// add nested menu toggler
|
||||
forEach(document.querySelectorAll('.menu-item>.nested-item'), ($nestedItem) => {
|
||||
$nestedItem.addEventListener('click', function () {
|
||||
@@ -213,10 +261,33 @@ class FixIt {
|
||||
const $searchLoading = document.getElementById(`search-loading-${suffix}`);
|
||||
const $searchClear = document.getElementById(`search-clear-${suffix}`);
|
||||
const $searchCancel = document.getElementById('search-cancel-mobile');
|
||||
const $mask = document.getElementById('mask');
|
||||
const $menuToggleMobile = document.getElementById('menu-toggle-mobile');
|
||||
const $menuMobile = document.getElementById('menu-mobile');
|
||||
if (!$header || !$searchInput || !$searchToggle || !$searchLoading || !$searchClear) return;
|
||||
const setSearchExpanded = (expanded) => {
|
||||
$searchToggle?.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
||||
};
|
||||
const overlayName = `search-${suffix}`;
|
||||
const openSearch = () => {
|
||||
if (_isMobile && $menuToggleMobile && $menuMobile) {
|
||||
this.disableScrollEvent = true;
|
||||
$menuToggleMobile.classList.add('active');
|
||||
$menuMobile.classList.add('active');
|
||||
$menuToggleMobile.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
$header.classList.add('open');
|
||||
setSearchExpanded(true);
|
||||
!_isMobile && $searchInput.focus();
|
||||
};
|
||||
const closeSearch = () => {
|
||||
if (_isMobile && $menuToggleMobile && $menuMobile) {
|
||||
this.disableScrollEvent = false;
|
||||
$menuToggleMobile.classList.remove('active');
|
||||
$menuMobile.classList.remove('active');
|
||||
$menuToggleMobile.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
this._resetSearchUI($header, $searchLoading, $searchClear, _isMobile ? this._searchMobile : this._searchDesktop);
|
||||
};
|
||||
|
||||
// goto the PostChat panel rather than search results
|
||||
if (searchConfig.type === 'post-chat' && window.postChatUser) {
|
||||
@@ -234,42 +305,36 @@ class FixIt {
|
||||
|
||||
if (_isMobile) {
|
||||
this._searchMobileOnce = true;
|
||||
this.registerMaskOverlay(overlayName, {
|
||||
isActive: () => $header.classList.contains('open'),
|
||||
onOpen: openSearch,
|
||||
onClose: closeSearch,
|
||||
});
|
||||
$searchInput.addEventListener('focus', () => {
|
||||
$mask.classList.add('blur');
|
||||
$header.classList.add('open');
|
||||
setSearchExpanded(true);
|
||||
this.openMaskOverlay(overlayName);
|
||||
}, false);
|
||||
$searchCancel.addEventListener('click', () => {
|
||||
$mask.classList.remove('blur');
|
||||
document.getElementById('menu-toggle-mobile').classList.remove('active');
|
||||
document.getElementById('menu-mobile').classList.remove('active');
|
||||
document.getElementById('menu-toggle-mobile').setAttribute('aria-expanded', 'false');
|
||||
this._resetSearchUI($header, $searchLoading, $searchClear, this._searchMobile);
|
||||
this.closeMaskOverlay(overlayName);
|
||||
}, false);
|
||||
$searchClear.addEventListener('click', () => {
|
||||
this.disableScrollEvent = false;
|
||||
$searchClear.style.display = 'none';
|
||||
this._searchMobile && this._searchMobile.autocomplete.setVal('');
|
||||
}, false);
|
||||
this._searchMobileOnClickMask = this._searchMobileOnClickMask || (() => {
|
||||
this._resetSearchUI($header, $searchLoading, $searchClear, this._searchMobile);
|
||||
});
|
||||
this.clickMaskEventSet.add(this._searchMobileOnClickMask);
|
||||
} else {
|
||||
this._searchDesktopOnce = true;
|
||||
this.registerMaskOverlay(overlayName, {
|
||||
isActive: () => $header.classList.contains('open'),
|
||||
onOpen: openSearch,
|
||||
onClose: closeSearch,
|
||||
});
|
||||
$searchToggle.addEventListener('click', () => {
|
||||
$mask.classList.add('blur');
|
||||
$header.classList.add('open');
|
||||
$searchInput.focus();
|
||||
setSearchExpanded(true);
|
||||
this.toggleMaskOverlay(overlayName);
|
||||
}, false);
|
||||
$searchClear.addEventListener('click', () => {
|
||||
$searchClear.style.display = 'none';
|
||||
this._searchDesktop && this._searchDesktop.autocomplete.setVal('');
|
||||
}, false);
|
||||
this._searchDesktopOnClickMask = this._searchDesktopOnClickMask || (() => {
|
||||
this._resetSearchUI($header, $searchLoading, $searchClear, this._searchDesktop);
|
||||
});
|
||||
this.clickMaskEventSet.add(this._searchDesktopOnClickMask);
|
||||
}
|
||||
$searchInput.addEventListener('input', () => {
|
||||
if ($searchInput.value === '') $searchClear.style.display = 'none';
|
||||
@@ -428,7 +493,7 @@ class FixIt {
|
||||
}
|
||||
);
|
||||
autosearch.on('autocomplete:selected', (_event, suggestion, _dataset, _context) => {
|
||||
$mask?.click();
|
||||
this.closeMaskOverlay(overlayName);
|
||||
window.location.assign(suggestion.uri);
|
||||
});
|
||||
if (_isMobile) {
|
||||
@@ -811,42 +876,121 @@ class FixIt {
|
||||
stagingDOM.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to update TOC active state based on scroll position
|
||||
* @param {HTMLElement} $tocContainer - TOC container element for parent traversal
|
||||
* @param {HTMLCollection} $headingElements - Heading elements to track
|
||||
* @param {number} indexOffset - Offset for active state calculation
|
||||
*/
|
||||
_updateTocActiveState($tocContainer, $headingElements, indexOffset) {
|
||||
const $tocLinkElements = $tocContainer.querySelectorAll('a:first-child');
|
||||
const $tocLiElements = $tocContainer.getElementsByTagName('li');
|
||||
getVisibleHeaderOffset() {
|
||||
const $desktopHeader = document.getElementById('header-desktop');
|
||||
const $mobileHeader = document.getElementById('header-mobile');
|
||||
const $header = [$desktopHeader, $mobileHeader].find(($el) => $el && window.getComputedStyle($el).display !== 'none');
|
||||
if (!$header) return 0;
|
||||
const isDesktop = $header.id === 'header-desktop';
|
||||
const headerMode = isDesktop ? document.body.dataset.headerDesktop : document.body.dataset.headerMobile;
|
||||
if (!['sticky', 'auto'].includes(headerMode)) return 0;
|
||||
if (headerMode === 'auto' && $header.classList.contains('header__fadeOutUp')) return 0;
|
||||
return $header.offsetHeight;
|
||||
}
|
||||
|
||||
// Remove all active classes
|
||||
forEach($tocLinkElements, ($tocLink) => {
|
||||
$tocLink.classList.remove('active');
|
||||
});
|
||||
forEach($tocLiElements, ($tocLi) => {
|
||||
$tocLi.classList.remove('has-active');
|
||||
});
|
||||
getBreadcrumbHeight() {
|
||||
return document.querySelector('.breadcrumb-container')?.offsetHeight || 0;
|
||||
}
|
||||
|
||||
// Calculate active TOC index
|
||||
let activeTocIndex = $headingElements.length - 1;
|
||||
for (let i = 0; i < $headingElements.length - 1; i++) {
|
||||
const thisTop = $headingElements[i].getBoundingClientRect().top;
|
||||
const nextTop = $headingElements[i + 1].getBoundingClientRect().top;
|
||||
if ((i == 0 && thisTop > indexOffset) || (thisTop <= indexOffset && nextTop > indexOffset)) {
|
||||
activeTocIndex = i;
|
||||
getTocIndexOffset() {
|
||||
return 20 + this.getVisibleHeaderOffset() + this.getBreadcrumbHeight();
|
||||
}
|
||||
|
||||
getTocHeadingElements() {
|
||||
return Array.from(document.getElementsByClassName('heading-element')).filter(($heading) => $heading.id);
|
||||
}
|
||||
|
||||
getActiveTocHeading($headingElements, indexOffset = this.getTocIndexOffset()) {
|
||||
if (!$headingElements.length) return null;
|
||||
const threshold = window.scrollY + indexOffset + 1;
|
||||
let $activeHeading = $headingElements[0];
|
||||
for (const $heading of $headingElements) {
|
||||
const headingTop = window.scrollY + $heading.getBoundingClientRect().top;
|
||||
if (headingTop <= threshold) {
|
||||
$activeHeading = $heading;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $activeHeading;
|
||||
}
|
||||
|
||||
// Add active classes
|
||||
if (activeTocIndex !== -1 && $tocLinkElements[activeTocIndex]) {
|
||||
$tocLinkElements[activeTocIndex].classList.add('active');
|
||||
let $parent = $tocLinkElements[activeTocIndex].parentElement;
|
||||
while ($parent !== $tocContainer) {
|
||||
getTocRoots() {
|
||||
return [
|
||||
document.getElementById('TableOfContents'),
|
||||
document.querySelector('#toc-content-static > nav'),
|
||||
document.querySelector('#toc-content-drawer > nav'),
|
||||
].filter(Boolean);
|
||||
}
|
||||
|
||||
getTocLinkById($tocRoot, id) {
|
||||
if (!$tocRoot || !id) return null;
|
||||
const targetHash = `#${id}`;
|
||||
return Array.from($tocRoot.querySelectorAll('a[href^="#"]')).find(($link) => $link.getAttribute('href') === targetHash) || null;
|
||||
}
|
||||
|
||||
applyTocActiveState($tocRoot, activeId) {
|
||||
if (!$tocRoot) return;
|
||||
forEach($tocRoot.querySelectorAll('a[href^="#"]'), ($tocLink) => {
|
||||
$tocLink.classList.remove('active');
|
||||
});
|
||||
forEach($tocRoot.querySelectorAll('li'), ($tocLi) => {
|
||||
$tocLi.classList.remove('has-active');
|
||||
});
|
||||
const $activeLink = this.getTocLinkById($tocRoot, activeId);
|
||||
if (!$activeLink) return;
|
||||
$activeLink.classList.add('active');
|
||||
let $parent = $activeLink.closest('li');
|
||||
while ($parent) {
|
||||
$parent.classList.add('has-active');
|
||||
$parent = $parent.parentElement.parentElement;
|
||||
$parent = $parent.parentElement?.closest('li') || null;
|
||||
}
|
||||
}
|
||||
|
||||
scrollActiveTocLinkIntoView($tocRoot, activeId, $scrollContainer = $tocRoot) {
|
||||
const $activeLink = this.getTocLinkById($tocRoot, activeId);
|
||||
if (!$activeLink || !$scrollContainer) return;
|
||||
const containerRect = $scrollContainer.getBoundingClientRect();
|
||||
const linkRect = $activeLink.getBoundingClientRect();
|
||||
const offsetTop = linkRect.top - containerRect.top;
|
||||
const offsetBottom = linkRect.bottom - containerRect.bottom;
|
||||
if (offsetTop < 0) {
|
||||
$scrollContainer.scrollTop += offsetTop;
|
||||
} else if (offsetBottom > 0) {
|
||||
$scrollContainer.scrollTop += offsetBottom;
|
||||
}
|
||||
}
|
||||
|
||||
syncTocHeight() {
|
||||
const $toc = document.getElementById('toc-auto');
|
||||
const $tocContentAuto = document.getElementById('toc-content-auto');
|
||||
if ($toc && $tocContentAuto) {
|
||||
const maxHeight = Math.max(window.innerHeight - $tocContentAuto.getBoundingClientRect().top - 16);
|
||||
$tocContentAuto.style.setProperty('--fi-toc-content-max-height', `${Math.floor(maxHeight)}px`);
|
||||
}
|
||||
}
|
||||
|
||||
syncTocActiveState() {
|
||||
const $headingElements = this.getTocHeadingElements();
|
||||
const $activeHeading = this.getActiveTocHeading($headingElements);
|
||||
if (!$activeHeading?.id) return;
|
||||
const activeId = $activeHeading.id;
|
||||
const $tocRoots = this.getTocRoots();
|
||||
forEach($tocRoots, ($tocRoot) => {
|
||||
this.applyTocActiveState($tocRoot, activeId);
|
||||
});
|
||||
if (this.activeTocId !== activeId) {
|
||||
this.activeTocId = activeId;
|
||||
if (!isTocStatic()) {
|
||||
const $autoTocRoot = document.getElementById('TableOfContents');
|
||||
const $autoTocContainer = document.getElementById('toc-content-auto');
|
||||
if ($autoTocRoot && $autoTocContainer) {
|
||||
this.scrollActiveTocLinkIntoView($autoTocRoot, activeId, $autoTocContainer);
|
||||
}
|
||||
}
|
||||
if (document.getElementById('toc-dialog')?.open) {
|
||||
const $dialogTocRoot = document.querySelector('#toc-content-drawer > nav');
|
||||
this.scrollActiveTocLinkIntoView($dialogTocRoot, activeId, $dialogTocRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -857,15 +1001,12 @@ class FixIt {
|
||||
initToc() {
|
||||
const $tocCore = document.getElementById('TableOfContents');
|
||||
if ($tocCore === null) return;
|
||||
const $headingElements = document.getElementsByClassName('heading-element');
|
||||
const INDEX_OFFSET = 20 + this.breadcrumbHeight + (
|
||||
document.body.dataset.headerDesktop !== 'normal' ? document.getElementById('header-desktop').offsetHeight : 0
|
||||
);
|
||||
// TOC Drawer Button Visibility
|
||||
const openButton = document.querySelector("#toc-drawer-button");
|
||||
if (openButton) {
|
||||
openButton.classList.toggle('d-none', !isTocStatic());
|
||||
}
|
||||
this.activeTocId = null;
|
||||
// TOC Static and TOC Dialog
|
||||
if (isTocStatic()) {
|
||||
const $tocContentStatic = document.getElementById('toc-content-static');
|
||||
@@ -873,16 +1014,8 @@ class FixIt {
|
||||
$tocCore.parentElement.removeChild($tocCore);
|
||||
$tocContentStatic.appendChild($tocCore);
|
||||
}
|
||||
this._tocDialogOnScroll = this._tocDialogOnScroll || (() => {
|
||||
this._updateTocActiveState(
|
||||
document.querySelector('#toc-content-drawer>nav'),
|
||||
$headingElements,
|
||||
INDEX_OFFSET
|
||||
);
|
||||
});
|
||||
this._tocDialogOnScroll();
|
||||
this.scrollEventSet.add(this._tocDialogOnScroll);
|
||||
this._tocOnScroll && this.scrollEventSet.delete(this._tocOnScroll);
|
||||
this.syncTocHeight();
|
||||
this.syncTocActiveState();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -895,12 +1028,8 @@ class FixIt {
|
||||
const $toc = document.getElementById('toc-auto');
|
||||
$toc.style.visibility = 'visible';
|
||||
animateCSS($toc, ['animate__fadeIn', 'animate__faster'], true);
|
||||
this._tocOnScroll = this._tocOnScroll || (() => {
|
||||
this._updateTocActiveState($tocCore, $headingElements, INDEX_OFFSET);
|
||||
});
|
||||
this._tocOnScroll();
|
||||
this.scrollEventSet.add(this._tocOnScroll);
|
||||
this._tocDialogOnScroll && this.scrollEventSet.delete(this._tocDialogOnScroll);
|
||||
this.syncTocHeight();
|
||||
this.syncTocActiveState();
|
||||
}
|
||||
|
||||
// TODO refactor use allow-discrete display property
|
||||
@@ -931,9 +1060,13 @@ class FixIt {
|
||||
openButton.addEventListener("click", () => {
|
||||
dialog.showModal();
|
||||
openButton.setAttribute('aria-expanded', 'true');
|
||||
this.syncTocHeight();
|
||||
this.syncTocActiveState();
|
||||
const $dialogTocRoot = document.querySelector('#toc-content-drawer > nav');
|
||||
this.scrollActiveTocLinkIntoView($dialogTocRoot, this.activeTocId, $dialogTocRoot);
|
||||
document.activeElement?.blur();
|
||||
});
|
||||
dialog.addEventListener("click", (e) => {
|
||||
dialog.addEventListener("click", () => {
|
||||
dialog.close();
|
||||
});
|
||||
dialog.addEventListener("close", () => {
|
||||
@@ -1473,7 +1606,6 @@ class FixIt {
|
||||
this.initMathJax();
|
||||
this.initJsonViewer();
|
||||
this.initTabEvents(target);
|
||||
this.initRoleButtons(target);
|
||||
FileTree.init(target);
|
||||
window.FixItMermaid?.init?.();
|
||||
window.FixItAPlayer?.init?.();
|
||||
@@ -1580,10 +1712,14 @@ class FixIt {
|
||||
scrollIntoView('body');
|
||||
});
|
||||
window.addEventListener('scroll', (event) => {
|
||||
if (this.disableScrollEvent) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
this.newScrollTop = getScrollTop();
|
||||
const scroll = this.newScrollTop - this.oldScrollTop;
|
||||
if (Math.abs(scroll) > ACCURACY) {
|
||||
document.getElementById('mask').click();
|
||||
this.closeActiveMaskOverlay();
|
||||
const isScrollingDown = scroll > 0;
|
||||
forEach($autoHeaders, ($header) => {
|
||||
if (isScrollingDown) {
|
||||
@@ -1627,6 +1763,8 @@ class FixIt {
|
||||
for (let event of this.scrollEventSet) {
|
||||
event();
|
||||
}
|
||||
this.syncTocHeight();
|
||||
this.syncTocActiveState();
|
||||
this.oldScrollTop = this.newScrollTop;
|
||||
}, false);
|
||||
}
|
||||
@@ -1642,10 +1780,12 @@ class FixIt {
|
||||
}
|
||||
this.initToc();
|
||||
this.initSearch();
|
||||
this.syncTocHeight();
|
||||
this.syncTocActiveState();
|
||||
|
||||
const _isMobile = isMobile();
|
||||
if (_isMobile !== resizeBefore) {
|
||||
document.getElementById('mask').click();
|
||||
this.closeActiveMaskOverlay();
|
||||
resizeBefore = _isMobile;
|
||||
}
|
||||
}, 100);
|
||||
@@ -1656,10 +1796,7 @@ class FixIt {
|
||||
onClickMask() {
|
||||
document.getElementById('mask').addEventListener('click', (e) => {
|
||||
if (!e.target.classList.contains('blur')) return;
|
||||
for (let event of this.clickMaskEventSet) {
|
||||
event();
|
||||
}
|
||||
e.target.classList.remove('blur');
|
||||
this.closeActiveMaskOverlay();
|
||||
}, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
{{- if $config.copyable -}}
|
||||
{{- $copyIcon := dict "Class" "fa-regular fa-clone fa-width-auto" | partial "plugin/icon.html" -}}
|
||||
{{- $copyBtn := printf `<div class="code-copy-btn" aria-label="%s" role="button" tabindex="0" title="%s">%s</div>` (T "assets.copyToClipboard") (T "assets.copyToClipboard") $copyIcon -}}
|
||||
{{- $copyBtn := printf `<button type="button" class="code-copy-btn" aria-label="%s" title="%s">%s</button>` (T "assets.copyToClipboard") (T "assets.copyToClipboard") $copyIcon -}}
|
||||
{{- $wrapper = replaceRE `(<div class="code-wrapper">[\s\S]*?<\/div>)` (printf `$1%s` $copyBtn) $wrapper -}}
|
||||
{{- end -}}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{{- /* Code expand button for code blocks */ -}}
|
||||
{{- $wrapper := .Wrapper -}}
|
||||
{{- $expandIcon := dict "Class" "fa-solid fa-angles-down" | partial "plugin/icon.html" -}}
|
||||
{{- $expandBtn := printf `<div class="code-expand-btn" aria-label="%s" role="button" tabindex="0">%s</div>` (T "assets.expandCode") $expandIcon -}}
|
||||
{{- $expandBtn := printf `<button type="button" class="code-expand-btn" aria-label="%s">%s</button>` (T "assets.expandCode") $expandIcon -}}
|
||||
{{- $wrapper = replaceRE `(<div class="code-wrapper">[\s\S]*?<\/div>)` (printf `$1%s` $expandBtn) $wrapper -}}
|
||||
|
||||
{{- return $wrapper -}}
|
||||
|
||||
@@ -11,34 +11,34 @@
|
||||
{{- $fullscreenBtn := "" -}}
|
||||
{{- $foldIcon := dict "Class" "arrow fa-solid fa-chevron-down" | partial "plugin/icon.html" -}}
|
||||
{{- $titleEl := printf `<span class="code-title">%s</span>` $foldIcon -}}
|
||||
{{- $ellipsisBtn := printf `<span class="ellipses-btn" aria-label="Show more options" role="button" tabindex="0">%s</span>` (dict "Class" "fa-solid fa-ellipsis-h" | partial "plugin/icon.html") -}}
|
||||
{{- $ellipsisBtn := printf `<button type="button" class="ellipses-btn" aria-label="Show more options">%s</button>` (dict "Class" "fa-solid fa-ellipsis-h" | partial "plugin/icon.html") -}}
|
||||
|
||||
{{- if $config.title -}}
|
||||
{{- $titleEl = printf `<span class="code-title">%s<span class="title-inner">%s</span></span>` $foldIcon $config.title -}}
|
||||
{{- end -}}
|
||||
{{- if $config.linenostoggler -}}
|
||||
{{- $lineNosIcon := dict "Class" "fa-solid fa-list-ol" | partial "plugin/icon.html" -}}
|
||||
{{- $lineNosBtn = printf `<span class="action-btn line-nos-btn" aria-label="%s" role="button" tabindex="0" title="%s">%s</span>` (T "assets.toggleLineNumbers") (T "assets.toggleLineNumbers") $lineNosIcon -}}
|
||||
{{- $lineNosBtn = printf `<button type="button" class="action-btn line-nos-btn" aria-label="%s" title="%s">%s</button>` (T "assets.toggleLineNumbers") (T "assets.toggleLineNumbers") $lineNosIcon -}}
|
||||
{{- end -}}
|
||||
{{- if $config.linewraptoggler -}}
|
||||
{{- $lineWrapIcon := dict "Class" "fa-solid fa-right-left" | partial "plugin/icon.html" -}}
|
||||
{{- $lineWrapBtn = printf `<span class="action-btn line-wrap-btn" aria-label="%s" role="button" tabindex="0" title="%s">%s</span>` (T "assets.toggleLineWrap") (T "assets.toggleLineWrap") $lineWrapIcon -}}
|
||||
{{- $lineWrapBtn = printf `<button type="button" class="action-btn line-wrap-btn" aria-label="%s" title="%s">%s</button>` (T "assets.toggleLineWrap") (T "assets.toggleLineWrap") $lineWrapIcon -}}
|
||||
{{- end -}}
|
||||
{{- if $config.editable -}}
|
||||
{{- $editIcon := dict "Class" "fa-solid fa-pen-to-square" | partial "plugin/icon.html" -}}
|
||||
{{- $editBtn = printf `<span class="action-btn edit-btn" aria-label="%s" role="button" tabindex="0" title="%s">%s</span>` (T "assets.toggleCodeEditable") (T "assets.toggleCodeEditable") $editIcon -}}
|
||||
{{- $editBtn = printf `<button type="button" class="action-btn edit-btn" aria-label="%s" title="%s">%s</button>` (T "assets.toggleCodeEditable") (T "assets.toggleCodeEditable") $editIcon -}}
|
||||
{{- end -}}
|
||||
{{- if $config.copyable -}}
|
||||
{{- $copyIcon := dict "Class" "fa-regular fa-clone" | partial "plugin/icon.html" -}}
|
||||
{{- $copyBtn = printf `<span class="action-btn copy-btn" aria-label="%s" role="button" tabindex="0" title="%s" data-copied-text="%s">%s</span>` (T "assets.copyToClipboard") (T "assets.copyToClipboard") (T "assets.copiedText") $copyIcon -}}
|
||||
{{- $copyBtn = printf `<button type="button" class="action-btn copy-btn" aria-label="%s" title="%s" data-copied-text="%s">%s</button>` (T "assets.copyToClipboard") (T "assets.copyToClipboard") (T "assets.copiedText") $copyIcon -}}
|
||||
{{- end -}}
|
||||
{{- if $config.downloadable -}}
|
||||
{{- $downloadIcon := dict "Class" "fa-solid fa-download" | partial "plugin/icon.html" -}}
|
||||
{{- $downloadBtn = printf `<span class="action-btn download-btn" aria-label="%s" role="button" tabindex="0" title="%s">%s</span>` (T "assets.downloadCode") (T "assets.downloadCode") $downloadIcon -}}
|
||||
{{- $downloadBtn = printf `<button type="button" class="action-btn download-btn" aria-label="%s" title="%s">%s</button>` (T "assets.downloadCode") (T "assets.downloadCode") $downloadIcon -}}
|
||||
{{- end -}}
|
||||
{{- if $config.fullscreen -}}
|
||||
{{- $fullscreenIcon := dict "Class" "fa-solid fa-expand" | partial "plugin/icon.html" -}}
|
||||
{{- $fullscreenBtn = printf `<span class="action-btn fullscreen-btn" aria-label="%s" role="button" tabindex="0" title="%s">%s</span>` (T "assets.toggleCodeFullscreen") (T "assets.toggleCodeFullscreen") $fullscreenIcon -}}
|
||||
{{- $fullscreenBtn = printf `<button type="button" class="action-btn fullscreen-btn" aria-label="%s" title="%s">%s</button>` (T "assets.toggleCodeFullscreen") (T "assets.toggleCodeFullscreen") $fullscreenIcon -}}
|
||||
{{- end -}}
|
||||
{{- with $config.name -}}
|
||||
{{- $titleEl = replace $titleEl `<span class="code-title">` (printf `<span class="code-title" data-name="%s">` .) -}}
|
||||
|
||||
@@ -108,14 +108,16 @@
|
||||
</span>
|
||||
</li>
|
||||
{{- end -}}
|
||||
<li class="menu-item theme-switch" role="button" tabindex="0" aria-label="{{ T "header.switchTheme" }}" title="{{ T "header.switchTheme" }}">
|
||||
<li class="menu-item theme-switch-item">
|
||||
<button type="button" class="theme-switch" aria-label="{{ T "header.switchTheme" }}" title="{{ T "header.switchTheme" }}">
|
||||
{{- dict "Class" "fa-solid fa-adjust" | partial "plugin/icon.html" -}}
|
||||
</button>
|
||||
</li>
|
||||
{{- if hugo.IsMultilingual -}}
|
||||
<li class="menu-item language-switch">
|
||||
<span role="button" tabindex="0" aria-label="{{ T "header.selectLanguage" }}" title="{{ T "header.selectLanguage" }}">
|
||||
<button type="button" aria-label="{{ T "header.selectLanguage" }}" title="{{ T "header.selectLanguage" }}">
|
||||
{{- dict "Class" "fa-solid fa-language" | partial "plugin/icon.html" -}}
|
||||
</span>
|
||||
</button>
|
||||
<ul class="sub-menu">
|
||||
{{- if (eq .Kind "404") -}}
|
||||
{{- range hugo.Sites -}}
|
||||
@@ -187,12 +189,12 @@
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
</div>
|
||||
<div class="theme-switch" role="button" tabindex="0" aria-label="{{ T "header.switchTheme" }}">
|
||||
<button type="button" class="theme-switch" aria-label="{{ T "header.switchTheme" }}">
|
||||
{{- dict "Class" "fa-solid fa-adjust" | partial "plugin/icon.html" -}}
|
||||
</div>
|
||||
<div class="menu-toggle" id="menu-toggle-mobile" role="button" tabindex="0" aria-controls="menu-mobile" aria-expanded="false" aria-labelledby="menu-mobile">
|
||||
</button>
|
||||
<button type="button" class="menu-toggle" id="menu-toggle-mobile" aria-controls="menu-mobile" aria-expanded="false" aria-labelledby="menu-mobile">
|
||||
<span></span><span></span><span></span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<nav>
|
||||
<ul class="menu" id="menu-mobile">
|
||||
@@ -286,10 +288,10 @@
|
||||
<li class="menu-item menu-system">
|
||||
{{- if hugo.IsMultilingual -}}
|
||||
<span class="menu-system-item language-switch">
|
||||
<span role="button" tabindex="0" aria-label="{{ T "header.selectLanguage" }}" title="{{ T "header.selectLanguage" }}">
|
||||
<button type="button" aria-label="{{ T "header.selectLanguage" }}" title="{{ T "header.selectLanguage" }}">
|
||||
{{- .Language.LanguageName -}}
|
||||
{{- dict "Class" "dropdown-icon fa-solid fa-chevron-down" | partial "plugin/icon.html" -}}
|
||||
</span>
|
||||
</button>
|
||||
<select id="language-select" class="language-select" onchange="location = this.value;">
|
||||
{{- if eq .Kind "404" -}}
|
||||
{{- range hugo.Sites -}}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="fixed-buttons">
|
||||
{{- /* back to top button */ -}}
|
||||
{{- if $backToTop.enable -}}
|
||||
<div class="fixed-button back-to-top animate__faster d-none" role="button" tabindex="0" aria-label="{{ T `baseof.backToTop` }}">
|
||||
<button type="button" class="fixed-button back-to-top animate__faster d-none" aria-label="{{ T `baseof.backToTop` }}">
|
||||
{{- dict "Class" "fa-solid fa-arrow-up" | partial "plugin/icon.html" -}}
|
||||
{{- if $backToTop.scrollpercent -}}
|
||||
<svg viewBox="0 0 100 100">
|
||||
@@ -14,19 +14,19 @@
|
||||
<circle class="progress" cx="50" cy="50" r="50"></circle>
|
||||
</svg>
|
||||
{{- end -}}
|
||||
</div>
|
||||
</button>
|
||||
{{- end -}}
|
||||
{{- /* toc dialog button */ -}}
|
||||
{{- if .Store.Get "showToc" -}}
|
||||
<div id="toc-drawer-button" class="fixed-button toc-drawer-button d-none{{ with $params.password }} encrypted-hidden{{ end }}" role="button" tabindex="0" aria-label="{{ T `single.contents` }}" aria-controls="toc-dialog" aria-expanded="false">
|
||||
<button type="button" id="toc-drawer-button" class="fixed-button toc-drawer-button d-none{{ with $params.password }} encrypted-hidden{{ end }}" aria-label="{{ T `single.contents` }}" aria-controls="toc-dialog" aria-expanded="false">
|
||||
{{- dict "Class" "fa-solid fa-bars" | partial "plugin/icon.html" -}}
|
||||
</div>
|
||||
</button>
|
||||
{{- end -}}
|
||||
{{- /* comment button */ -}}
|
||||
{{- if $comment.enable -}}
|
||||
<div class="fixed-button view-comments d-none" role="button" tabindex="0" aria-label="{{ T `baseof.viewComments` }}">
|
||||
<button type="button" class="fixed-button view-comments d-none" aria-label="{{ T `baseof.viewComments` }}">
|
||||
{{- dict "Class" "fa-solid fa-comment" | partial "plugin/icon.html" -}}
|
||||
</div>
|
||||
</button>
|
||||
{{- end -}}
|
||||
</div>
|
||||
{{- end -}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{{- /* Diagram copy button */ -}}
|
||||
{{- $label := .Label | default "Copy Diagram code" -}}
|
||||
|
||||
<div class="diagram-copy-btn" aria-label="{{ $label }}" role="button" tabindex="0">
|
||||
<button type="button" class="diagram-copy-btn" aria-label="{{ $label }}">
|
||||
{{- dict "Class" "fa-regular fa-clone fa-width-auto" | partial "plugin/icon.html" -}}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user