togglebutton.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /**
  2. * Add Toggle Buttons to elements
  3. */
  4. let toggleChevron = `
  5. <svg xmlns="http://www.w3.org/2000/svg" class="tb-icon toggle-chevron-right" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#000000" fill="none" stroke-linecap="round" stroke-linejoin="round">
  6. <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
  7. <polyline points="9 6 15 12 9 18" />
  8. </svg>`;
  9. var initToggleItems = () => {
  10. var itemsToToggle = document.querySelectorAll(togglebuttonSelector);
  11. console.log(`[togglebutton]: Adding toggle buttons to ${itemsToToggle.length} items`)
  12. // Add the button to each admonition and hook up a callback to toggle visibility
  13. itemsToToggle.forEach((item, index) => {
  14. if (item.classList.contains("admonition")) {
  15. // If it's an admonition block, then we'll add a button inside
  16. // Generate unique IDs for this item
  17. var toggleID = `toggle-${index}`;
  18. var buttonID = `button-${toggleID}`;
  19. item.setAttribute('id', toggleID);
  20. if (!item.classList.contains("toggle")){
  21. item.classList.add("toggle");
  22. }
  23. // This is the button that will be added to each item to trigger the toggle
  24. var collapseButton = `
  25. <button type="button" id="${buttonID}" class="toggle-button" data-target="${toggleID}" data-button="${buttonID}", data-toggle-hint="${toggleHintShow}">
  26. ${toggleChevron}
  27. </button>`;
  28. item.insertAdjacentHTML("afterbegin", collapseButton);
  29. thisButton = document.getElementById(buttonID);
  30. // Add click handlers for the button + admonition title (if admonition)
  31. thisButton.addEventListener('click', toggleClickHandler);
  32. admonitionTitle = document.querySelector(`#${toggleID} > .admonition-title`)
  33. if (admonitionTitle) {
  34. admonitionTitle.addEventListener('click', toggleClickHandler);
  35. admonitionTitle.dataset.target = toggleID
  36. admonitionTitle.dataset.button = buttonID
  37. }
  38. // Now hide the item for this toggle button unless explicitly noted to show
  39. if (!item.classList.contains("toggle-shown")) {
  40. toggleHidden(thisButton);
  41. }
  42. } else {
  43. // If not an admonition, wrap the block in a <details> block
  44. // Define the structure of the details block and insert it as a sibling
  45. var detailsBlock = `
  46. <details class="toggle-details">
  47. <summary>
  48. ${toggleChevron}
  49. <span>${toggleHintShow}</span>
  50. </summary>
  51. </details>`;
  52. item.insertAdjacentHTML("beforebegin", detailsBlock);
  53. // Now move the toggle-able content inside of the details block
  54. details = item.previousElementSibling
  55. details.appendChild(item)
  56. // Set up a click trigger to change the text as needed
  57. details.addEventListener('click', (click) => {
  58. let parent = click.target.parentElement;
  59. if (parent.tagName.toLowerCase() == "details") {
  60. summary = parent.querySelector("summary");
  61. details = parent;
  62. } else {
  63. summary = parent;
  64. details = parent.parentElement;
  65. }
  66. // Update the inner text for the proper hint
  67. if (details.open) {
  68. summary.querySelector("span").innerText = toggleHintShow;
  69. } else {
  70. summary.querySelector("span").innerText = toggleHintHide;
  71. }
  72. });
  73. // If we have a toggle-shown class, open details block should be open
  74. if (item.classList.contains("toggle-shown")) {
  75. details.click();
  76. }
  77. }
  78. })
  79. };
  80. // This should simply add / remove the collapsed class and change the button text
  81. var toggleHidden = (button) => {
  82. target = button.dataset['target']
  83. var itemToToggle = document.getElementById(target);
  84. if (itemToToggle.classList.contains("toggle-hidden")) {
  85. itemToToggle.classList.remove("toggle-hidden");
  86. button.classList.remove("toggle-button-hidden");
  87. } else {
  88. itemToToggle.classList.add("toggle-hidden");
  89. button.classList.add("toggle-button-hidden");
  90. }
  91. }
  92. var toggleClickHandler = (click) => {
  93. if (click.target.classList.contains("admonition-title")) {
  94. // If it's an admonition title, the button will be just before
  95. button = click.target.previousElementSibling;
  96. } else {
  97. // If not, we've clicked the button itself or its content, so search upwards
  98. button = click.currentTarget;
  99. }
  100. target = document.getElementById(button.dataset['button']);
  101. toggleHidden(target);
  102. }
  103. // If we want to blanket-add toggle classes to certain cells
  104. var addToggleToSelector = () => {
  105. const selector = "";
  106. if (selector.length > 0) {
  107. document.querySelectorAll(selector).forEach((item) => {
  108. item.classList.add("toggle");
  109. })
  110. }
  111. }
  112. // Helper function to run when the DOM is finished
  113. const sphinxToggleRunWhenDOMLoaded = cb => {
  114. if (document.readyState != 'loading') {
  115. cb()
  116. } else if (document.addEventListener) {
  117. document.addEventListener('DOMContentLoaded', cb)
  118. } else {
  119. document.attachEvent('onreadystatechange', function() {
  120. if (document.readyState == 'complete') cb()
  121. })
  122. }
  123. }
  124. sphinxToggleRunWhenDOMLoaded(addToggleToSelector)
  125. sphinxToggleRunWhenDOMLoaded(initToggleItems)
  126. /** Toggle details blocks to be open when printing */
  127. if (toggleOpenOnPrint == "true") {
  128. window.addEventListener("beforeprint", () => {
  129. // Open the details
  130. document.querySelectorAll("details.toggle-details").forEach((el) => {
  131. el.dataset["togglestatus"] = el.open;
  132. el.open = true;
  133. });
  134. // Open the admonitions
  135. document.querySelectorAll(".admonition.toggle.toggle-hidden").forEach((el) => {
  136. console.log(el);
  137. el.querySelector("button.toggle-button").click();
  138. el.dataset["toggle_after_print"] = "true";
  139. });
  140. });
  141. window.addEventListener("afterprint", () => {
  142. // Re-close the details that were closed
  143. document.querySelectorAll("details.toggle-details").forEach((el) => {
  144. el.open = el.dataset["togglestatus"] == "true";
  145. delete el.dataset["togglestatus"];
  146. });
  147. // Re-close the admonition toggle buttons
  148. document.querySelectorAll(".admonition.toggle").forEach((el) => {
  149. if (el.dataset["toggle_after_print"] == "true") {
  150. el.querySelector("button.toggle-button").click();
  151. delete el.dataset["toggle_after_print"];
  152. }
  153. });
  154. });
  155. }