Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better mobile device detection for interactive examples buttons #250

9 changes: 0 additions & 9 deletions jupyterlite_sphinx/jupyterlite_sphinx.css
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,3 @@
transform: rotate(1turn);
}
}

/* we do not want the button to show on smaller screens (phones), as clicking
* can download a lot of data. 480px is a commonly used breakpoint to identify if a device is a smartphone. */

@media (max-width: 480px), (max-height: 480px) {
div.try_examples_button_container {
display: none;
}
}
81 changes: 81 additions & 0 deletions jupyterlite_sphinx/jupyterlite_sphinx.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,71 @@ var tryExamplesGlobalMinHeight = 0;
*/
var tryExamplesConfigLoaded = false;

// This function is used to check if the current device is a mobile device.
// We assume the authenticity of the user agent string is enough to
// determine that, and we also check the window size as a fallback.
window.isMobileDevice = (() => {
let cachedUAResult = null;
let hasLogged = false;

const checkUserAgent = () => {
if (cachedUAResult !== null) {
return cachedUAResult;
}

const mobilePatterns = [
/Android/i,
/webOS/i,
/iPhone/i,
/iPad/i,
/iPod/i,
/BlackBerry/i,
/IEMobile/i,
/Windows Phone/i,
/Opera Mini/i,
/SamsungBrowser/i,
/UC.*Browser|UCWEB/i,
/MiuiBrowser/i,
/Mobile/i,
/Tablet/i,
];

cachedUAResult = mobilePatterns.some((pattern) =>
pattern.test(navigator.userAgent),
);
return cachedUAResult;
};

return () => {
const isMobileBySize =
window.innerWidth <= 480 || window.innerHeight <= 480;
const isLikelyMobile = checkUserAgent() || isMobileBySize;

if (isLikelyMobile && !hasLogged) {
console.log(
"Either a mobile device detected or the screen was resized. Disabling interactive example buttons to conserve bandwidth.",
);
hasLogged = true;
}

return isLikelyMobile;
};
})();

// A config loader with request deduplication + permanent caching
const ConfigLoader = (() => {
let configLoadPromise = null;

const loadConfig = async (configFilePath) => {
if (window.isMobileDevice()) {
const buttons = document.getElementsByClassName("try_examples_button");
for (let i = 0; i < buttons.length; i++) {
buttons[i].classList.add("hidden");
}
tryExamplesConfigLoaded = true; // mock it
return;
}

if (tryExamplesConfigLoaded) {
return;
}
Expand Down Expand Up @@ -229,6 +289,27 @@ const ConfigLoader = (() => {
};
})();

// Add a resize handler that will update the buttons' visibility on
// orientation changes
let resizeTimeout;
window.addEventListener("resize", () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
if (!tryExamplesConfigLoaded) return; // since we won't interfere if the config isn't loaded

const buttons = document.getElementsByClassName("try_examples_button");
const shouldHide = window.isMobileDevice();

for (let i = 0; i < buttons.length; i++) {
if (shouldHide) {
buttons[i].classList.add("hidden");
} else {
buttons[i].classList.remove("hidden");
}
}
}, 250);
});

window.loadTryExamplesConfig = ConfigLoader.loadConfig;

window.toggleTryExamplesButtons = () => {
Expand Down
Loading