From af9db4b88250f613be5f254f46f3d981de197b94 Mon Sep 17 00:00:00 2001 From: Christoph Froehlich Date: Mon, 1 Jan 2024 22:18:44 +0000 Subject: [PATCH] Rewrite acknowledgement page to use tabs --- _static/tabs.js | 147 ++++++++++++++++++++++ doc/acknowledgements/acknowledgements.rst | 69 +++++----- 2 files changed, 180 insertions(+), 36 deletions(-) create mode 100644 _static/tabs.js diff --git a/_static/tabs.js b/_static/tabs.js new file mode 100644 index 00000000000..f6b653f9a09 --- /dev/null +++ b/_static/tabs.js @@ -0,0 +1,147 @@ +try { + var session = window.sessionStorage || {}; +} catch (e) { + var session = {}; +} + +window.addEventListener("DOMContentLoaded", () => { + const allTabs = document.querySelectorAll('.sphinx-tabs-tab'); + const tabLists = document.querySelectorAll('[role="tablist"]'); + + allTabs.forEach(tab => { + tab.addEventListener("click", changeTabs); + }); + + tabLists.forEach(tabList => { + tabList.addEventListener("keydown", keyTabs); + }); + + // Restore group tab selection from session + const lastSelected = session.getItem('sphinx-tabs-last-selected'); + if (lastSelected != null) selectNamedTabs(lastSelected); +}); + +/** + * Key focus left and right between sibling elements using arrows + * @param {Node} e the element in focus when key was pressed + */ +function keyTabs(e) { + const tab = e.target; + let nextTab = null; + if (e.keyCode === 39 || e.keyCode === 37) { + tab.setAttribute("tabindex", -1); + // Move right + if (e.keyCode === 39) { + nextTab = tab.nextElementSibling; + if (nextTab === null) { + nextTab = tab.parentNode.firstElementChild; + } + // Move left + } else if (e.keyCode === 37) { + nextTab = tab.previousElementSibling; + if (nextTab === null) { + nextTab = tab.parentNode.lastElementChild; + } + } + } + + if (nextTab !== null) { + nextTab.setAttribute("tabindex", 0); + nextTab.focus(); + } +} + +/** + * Select or deselect clicked tab. If a group tab + * is selected, also select tab in other tabLists. + * @param {Node} e the element that was clicked + */ +function changeTabs(e) { + // Use this instead of the element that was clicked, in case it's a child + const notSelected = this.getAttribute("aria-selected") === "false"; + const positionBefore = this.parentNode.getBoundingClientRect().top; + const notClosable = !this.parentNode.classList.contains("closeable"); + + deselectTabList(this); + + if (notSelected || notClosable) { + selectTab(this); + const name = this.getAttribute("name"); + selectNamedTabs(name, this.id); + + if (this.classList.contains("group-tab")) { + // Persist during session + session.setItem('sphinx-tabs-last-selected', name); + } + } + + const positionAfter = this.parentNode.getBoundingClientRect().top; + const positionDelta = positionAfter - positionBefore; + // Scroll to offset content resizing + window.scrollTo(0, window.scrollY + positionDelta); +} + +/** + * Select tab and show associated panel. + * @param {Node} tab tab to select + */ +function selectTab(tab) { + tab.setAttribute("aria-selected", true); + + // Show the associated panel + document + .getElementById(tab.getAttribute("aria-controls")) + .removeAttribute("hidden"); +} + +/** + * Hide the panels associated with all tabs within the + * tablist containing this tab. + * @param {Node} tab a tab within the tablist to deselect + */ +function deselectTabList(tab) { + const parent = tab.parentNode; + const grandparent = parent.parentNode; + + Array.from(parent.children) + .forEach(t => t.setAttribute("aria-selected", false)); + + Array.from(grandparent.children) + .slice(1) // Skip tablist + .forEach(panel => panel.setAttribute("hidden", true)); +} + +/** + * Select grouped tabs with the same name, but no the tab + * with the given id. + * @param {Node} name name of grouped tab to be selected + * @param {Node} clickedId id of clicked tab + */ +function selectNamedTabs(name, clickedId=null) { + const groupedTabs = document.querySelectorAll(`.sphinx-tabs-tab[name="${name}"]`); + const tabLists = Array.from(groupedTabs).map(tab => tab.parentNode); + + tabLists + .forEach(tabList => { + // Don't want to change the tabList containing the clicked tab + const clickedTab = tabList.querySelector(`[id="${clickedId}"]`); + if (clickedTab === null ) { + // Select first tab with matching name + const tab = tabList.querySelector(`.sphinx-tabs-tab[name="${name}"]`); + deselectTabList(tab); + selectTab(tab); + } + }) +} + +// TODO(christophfroehlich) this has to be uncommented for jQuery of our code to work on the same page + +// if (typeof exports === 'undefined') { +// exports = {}; +// } + +// exports.keyTabs = keyTabs; +// exports.changeTabs = changeTabs; +// exports.selectTab = selectTab; +// exports.deselectTabList = deselectTabList; +// exports.selectNamedTabs = selectNamedTabs; diff --git a/doc/acknowledgements/acknowledgements.rst b/doc/acknowledgements/acknowledgements.rst index 637e4567bbf..3dedb6753da 100644 --- a/doc/acknowledgements/acknowledgements.rst +++ b/doc/acknowledgements/acknowledgements.rst @@ -5,58 +5,55 @@ Acknowledgements Maintainers ---------------- -The following people were maintaining the ``ros2_control`` framework, showing their all-time review activity: +The following people were maintaining the ``ros2_control`` framework, showing their review activity and contributions: -.. raw:: html - :file: reviewers_maintainers_stats.html +.. tabs:: -and their all-time contributions: + .. tab:: All-Time Reviews -.. raw:: html - :file: contributors_maintainers_stats.html + .. raw:: html + :file: reviewers_maintainers_stats.html -Activity during the past 12 months: + .. tab:: 12 Months -.. raw:: html - :file: reviewers_maintainers_stats_recent.html + .. raw:: html + :file: reviewers_maintainers_stats_recent.html -.. raw:: html - :file: contributors_maintainers_stats_recent.html + .. tab:: All-Time Contributions -Reviewers' Stats + .. raw:: html + :file: contributors_maintainers_stats.html + + .. tab:: 12 Months + + .. raw:: html + :file: contributors_maintainers_stats_recent.html + +Contributors ---------------- -The following people have contributed to the development of this project by giving valuable reviews for pull requests, see :ref:`doc/contributing/contributing:contributing` for more information. +The following people have contributed to the development of this project by providing valuable reviews or by submitting pull requests, see :ref:`doc/contributing/contributing:contributing` for more information. -.. raw:: html - :file: reviewers_stats.html +.. tabs:: -and with their contributions: + .. tab:: All-Time Reviews -.. raw:: html - :file: contributors_stats.html + .. raw:: html + :file: reviewers_stats.html -Activity during the past 12 months: + .. tab:: 12 Months -.. raw:: html - :file: reviewers_stats_recent.html + .. raw:: html + :file: reviewers_stats_recent.html -.. raw:: html - :file: contributors_stats_recent.html + .. tab:: All-Time Contributions -Contributors ----------------- + .. raw:: html + :file: contributors_stats.html + + .. tab:: 12 Months -The following links lists people who have contributed to the development of this project by submitting pull requests to the respective repository, see :ref:`doc/contributing/contributing:contributing` for more information. - -* `ros2_control `_ -* `ros2_controllers `_ -* `ros2_control_demos `_ -* `control_toolbox `_ -* `gazebo_ros2_control `_ -* `gz_ros2_control `_ -* `realtime_tools `_ -* `kinematics_interface `_ -* `control_msgs `_ + .. raw:: html + :file: contributors_stats_recent.html Companies and Institutions