Files
hextra/assets/js/core/tabs.js
Xin 62196dd666 feat(tabs): add icon support for tabs (#926)
* feat(tabs): add icon support for tabs

- Introduced an `icon` property for tabs to display icons alongside tab labels.
- Updated the tabs shortcode and HTML partials to accommodate the new icon feature.
- Enhanced documentation for tabs to include examples of using icons in tab definitions.

* chore: rebuild css

* doc(tabs): add icon support in tab documentation for multiple languages

- Enhanced documentation for tabs to include examples of using the `icon` parameter in Persian, Japanese, and Simplified Chinese.
- Updated the tab shortcode examples to demonstrate how to display icons alongside tab labels.

* chore: regenerate compiled css

* fix(a11y): add aria-hidden to decorative tab icons

Add aria-hidden="true" to tab icon SVGs so screen readers skip
decorative icons and only announce the tab name. Update @example
docblock in tab.html to demonstrate the icon parameter.

* doc(tabs): improve icon section with better examples and icon page link

Rename "Tabs With Icons" heading to "Add Icons" for consistency with
other action-oriented section titles. Replace JSON/YAML/TOML icon
example with Photos/Music/Videos using photograph, music-note, and
film icons that naturally match their labels. Add link to the Icon
shortcode page so users can discover available icon names.
2026-02-14 21:23:25 +00:00

103 lines
3.4 KiB
JavaScript

(function () {
function updateGroup(container, index) {
const tabs = Array.from(container.querySelectorAll('.hextra-tabs-toggle'));
tabs.forEach((tab, i) => {
tab.dataset.state = i === index ? 'selected' : '';
if (i === index) {
tab.setAttribute('aria-selected', 'true');
tab.tabIndex = 0;
} else {
tab.setAttribute('aria-selected', 'false');
tab.tabIndex = -1;
}
});
const panelsContainer = container.parentElement.nextElementSibling;
if (!panelsContainer) return;
Array.from(panelsContainer.children).forEach((panel, i) => {
panel.dataset.state = i === index ? 'selected' : '';
panel.setAttribute('aria-hidden', i === index ? 'false' : 'true');
if (i === index) {
panel.tabIndex = 0;
} else {
panel.removeAttribute('tabindex');
}
});
}
const syncGroups = document.querySelectorAll('[data-tab-group]');
syncGroups.forEach((group) => {
const key = encodeURIComponent(group.dataset.tabGroup);
const saved = localStorage.getItem('hextra-tab-' + key);
if (saved !== null) {
updateGroup(group, parseInt(saved, 10));
}
});
document.querySelectorAll('.hextra-tabs-toggle').forEach((button) => {
button.addEventListener('click', function (e) {
const targetButton = e.currentTarget;
const container = targetButton.parentElement;
const index = Array.from(container.querySelectorAll('.hextra-tabs-toggle')).indexOf(
targetButton
);
if (container.dataset.tabGroup) {
// Sync behavior: update all tab groups with the same name
const tabGroupValue = container.dataset.tabGroup;
const key = encodeURIComponent(tabGroupValue);
document
.querySelectorAll('[data-tab-group="' + tabGroupValue + '"]')
.forEach((grp) => updateGroup(grp, index));
localStorage.setItem('hextra-tab-' + key, index.toString());
} else {
// Non-sync behavior: update only this specific tab group
updateGroup(container, index);
}
});
// Keyboard navigation for tabs
button.addEventListener('keydown', function (e) {
const container = button.parentElement;
const tabs = Array.from(container.querySelectorAll('.hextra-tabs-toggle'));
const currentIndex = tabs.indexOf(button);
let newIndex;
switch (e.key) {
case 'ArrowRight':
case 'ArrowDown':
e.preventDefault();
newIndex = (currentIndex + 1) % tabs.length;
break;
case 'ArrowLeft':
case 'ArrowUp':
e.preventDefault();
newIndex = (currentIndex - 1 + tabs.length) % tabs.length;
break;
case 'Home':
e.preventDefault();
newIndex = 0;
break;
case 'End':
e.preventDefault();
newIndex = tabs.length - 1;
break;
default:
return;
}
if (container.dataset.tabGroup) {
const tabGroupValue = container.dataset.tabGroup;
const key = encodeURIComponent(tabGroupValue);
document
.querySelectorAll('[data-tab-group="' + tabGroupValue + '"]')
.forEach((grp) => updateGroup(grp, newIndex));
localStorage.setItem('hextra-tab-' + key, newIndex.toString());
} else {
updateGroup(container, newIndex);
}
tabs[newIndex].focus();
});
});
})();