Skip to content

Commit fb52264

Browse files
Better mobile device detection for interactive examples buttons (#250)
1 parent 4059e7a commit fb52264

File tree

2 files changed

+81
-9
lines changed

2 files changed

+81
-9
lines changed

jupyterlite_sphinx/jupyterlite_sphinx.css

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,3 @@
6767
transform: rotate(1turn);
6868
}
6969
}
70-
71-
/* we do not want the button to show on smaller screens (phones), as clicking
72-
* can download a lot of data. 480px is a commonly used breakpoint to identify if a device is a smartphone. */
73-
74-
@media (max-width: 480px), (max-height: 480px) {
75-
div.try_examples_button_container {
76-
display: none;
77-
}
78-
}

jupyterlite_sphinx/jupyterlite_sphinx.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,71 @@ var tryExamplesGlobalMinHeight = 0;
149149
*/
150150
var tryExamplesConfigLoaded = false;
151151

152+
// This function is used to check if the current device is a mobile device.
153+
// We assume the authenticity of the user agent string is enough to
154+
// determine that, and we also check the window size as a fallback.
155+
window.isMobileDevice = (() => {
156+
let cachedUAResult = null;
157+
let hasLogged = false;
158+
159+
const checkUserAgent = () => {
160+
if (cachedUAResult !== null) {
161+
return cachedUAResult;
162+
}
163+
164+
const mobilePatterns = [
165+
/Android/i,
166+
/webOS/i,
167+
/iPhone/i,
168+
/iPad/i,
169+
/iPod/i,
170+
/BlackBerry/i,
171+
/IEMobile/i,
172+
/Windows Phone/i,
173+
/Opera Mini/i,
174+
/SamsungBrowser/i,
175+
/UC.*Browser|UCWEB/i,
176+
/MiuiBrowser/i,
177+
/Mobile/i,
178+
/Tablet/i,
179+
];
180+
181+
cachedUAResult = mobilePatterns.some((pattern) =>
182+
pattern.test(navigator.userAgent),
183+
);
184+
return cachedUAResult;
185+
};
186+
187+
return () => {
188+
const isMobileBySize =
189+
window.innerWidth <= 480 || window.innerHeight <= 480;
190+
const isLikelyMobile = checkUserAgent() || isMobileBySize;
191+
192+
if (isLikelyMobile && !hasLogged) {
193+
console.log(
194+
"Either a mobile device detected or the screen was resized. Disabling interactive example buttons to conserve bandwidth.",
195+
);
196+
hasLogged = true;
197+
}
198+
199+
return isLikelyMobile;
200+
};
201+
})();
202+
152203
// A config loader with request deduplication + permanent caching
153204
const ConfigLoader = (() => {
154205
let configLoadPromise = null;
155206

156207
const loadConfig = async (configFilePath) => {
208+
if (window.isMobileDevice()) {
209+
const buttons = document.getElementsByClassName("try_examples_button");
210+
for (let i = 0; i < buttons.length; i++) {
211+
buttons[i].classList.add("hidden");
212+
}
213+
tryExamplesConfigLoaded = true; // mock it
214+
return;
215+
}
216+
157217
if (tryExamplesConfigLoaded) {
158218
return;
159219
}
@@ -229,6 +289,27 @@ const ConfigLoader = (() => {
229289
};
230290
})();
231291

292+
// Add a resize handler that will update the buttons' visibility on
293+
// orientation changes
294+
let resizeTimeout;
295+
window.addEventListener("resize", () => {
296+
clearTimeout(resizeTimeout);
297+
resizeTimeout = setTimeout(() => {
298+
if (!tryExamplesConfigLoaded) return; // since we won't interfere if the config isn't loaded
299+
300+
const buttons = document.getElementsByClassName("try_examples_button");
301+
const shouldHide = window.isMobileDevice();
302+
303+
for (let i = 0; i < buttons.length; i++) {
304+
if (shouldHide) {
305+
buttons[i].classList.add("hidden");
306+
} else {
307+
buttons[i].classList.remove("hidden");
308+
}
309+
}
310+
}, 250);
311+
});
312+
232313
window.loadTryExamplesConfig = ConfigLoader.loadConfig;
233314

234315
window.toggleTryExamplesButtons = () => {

0 commit comments

Comments
 (0)