diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.css b/jupyterlite_sphinx/jupyterlite_sphinx.css index ca5d462..a5ec462 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.css +++ b/jupyterlite_sphinx/jupyterlite_sphinx.css @@ -58,6 +58,7 @@ background: radial-gradient(farthest-side, #ffa516 94%, #0000) top/8px 8px no-repeat, conic-gradient(#0000 30%, #ffa516); + mask: radial-gradient(farthest-side, #0000 calc(100% - 8px), #000 0); -webkit-mask: radial-gradient(farthest-side, #0000 calc(100% - 8px), #000 0); animation: l13 1s infinite linear; } @@ -75,3 +76,25 @@ display: none; } } + +/* Some improvements for hidden buttons */ + +/* smooth transitions */ +.try_examples_button { + opacity: 1; + transition: opacity 0.3s ease-in-out; +} + +.try_examples_button.hidden { + display: none; + opacity: 0; +} + +.try_examples_button.fade-in { + transition: opacity 0.3s ease-in-out; +} + +/* to ensure hidden class takes precedence */ +.hidden { + display: none !important; +} diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.js b/jupyterlite_sphinx/jupyterlite_sphinx.js index 4ae8c96..9c34581 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.js +++ b/jupyterlite_sphinx/jupyterlite_sphinx.js @@ -163,8 +163,30 @@ window.loadTryExamplesConfig = async (configFilePath) => { const response = await fetch(configFileUrl); if (!response.ok) { if (response.status === 404) { - // Try examples ignore file is not present. - console.log("Optional try_examples config file not found."); + // Try examples ignore file is not present. Enable all interactive examples + // in that case. + console.log( + "Optional try_examples config file not found. Enabling all interactive examples.", + ); + + // Grab all hidden try examples buttons and fade them in + const buttons = document.getElementsByClassName( + "try_examples_button hidden", + ); + + requestAnimationFrame(() => { + for (let button of buttons) { + button.style.opacity = "0"; + button.classList.remove("hidden"); + button.classList.add("fade-in"); + requestAnimationFrame(() => { + button.style.opacity = "1"; + }); + } + }); + + tryExamplesConfigLoaded = true; + return; } throw new Error(`Error fetching ${configFilePath}`); @@ -172,6 +194,17 @@ window.loadTryExamplesConfig = async (configFilePath) => { const data = await response.json(); if (!data) { + // If config file exists but is empty, treat it like a missing config + // and enable all interactive examples since there is no ignore pattern + console.log( + "Try examples config file is empty. Enabling all interactive examples.", + ); + const buttons = document.getElementsByClassName( + "try_examples_button hidden", + ); + for (let button of buttons) { + button.classList.remove("hidden"); + } return; } @@ -180,21 +213,43 @@ window.loadTryExamplesConfig = async (configFilePath) => { tryExamplesGlobalMinHeight = parseInt(data.global_min_height); } - // Disable interactive examples if file matches one of the ignore patterns - // by hiding try_examples_buttons. - Patterns = data.ignore_patterns; + // Selectively enable interactive examples if file matches one of the ignore patterns + // by un-hiding try_examples_buttons with a smooth transition + const Patterns = data.ignore_patterns || []; + let shouldShowButtons = true; + for (let pattern of Patterns) { let regex = new RegExp(pattern); if (regex.test(currentPageUrl)) { - var buttons = document.getElementsByClassName("try_examples_button"); - for (var i = 0; i < buttons.length; i++) { - buttons[i].classList.add("hidden"); - } + shouldShowButtons = false; break; } } + + if (shouldShowButtons) { + const buttons = document.getElementsByClassName( + "try_examples_button hidden", + ); + requestAnimationFrame(() => { + for (let button of buttons) { + button.style.opacity = "0"; + button.classList.remove("hidden"); + button.classList.add("fade-in"); + requestAnimationFrame(() => { + button.style.opacity = "1"; + }); + } + }); + } } catch (error) { - console.error(error); + // On error, enable all buttons as a fallback to maintain current behavior + console.error("Error loading try_examples config:", error); + const buttons = document.getElementsByClassName( + "try_examples_button hidden", + ); + for (let button of buttons) { + button.classList.remove("hidden"); + } } tryExamplesConfigLoaded = true; }; @@ -202,7 +257,7 @@ window.loadTryExamplesConfig = async (configFilePath) => { window.toggleTryExamplesButtons = () => { /* Toggle visibility of TryExamples buttons. For use in console for debug * purposes. */ - var buttons = document.getElementsByClassName("try_examples_button"); + var buttons = document.getElementsByClassName("try_examples_button hidden"); for (var i = 0; i < buttons.length; i++) { buttons[i].classList.toggle("hidden"); diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.py b/jupyterlite_sphinx/jupyterlite_sphinx.py index 366b0e9..2a2e936 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.py +++ b/jupyterlite_sphinx/jupyterlite_sphinx.py @@ -553,15 +553,16 @@ def run(self): ) # Button with the onclick event to swap embedded notebook back to examples. + # This includes a 'hidden' class by default to hide until the page is loaded. go_back_button_html = ( - '" ) full_screen_button_html = ( - '" @@ -570,7 +571,7 @@ def run(self): # Button with the onclick event to swap examples with embedded notebook. try_it_button_html = ( '