Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 1 addition & 63 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2023 The Pybricks Authors
// Copyright (c) 2020-2026 The Pybricks Authors

import 'react-splitter-layout/lib/index.css';
import './app.scss';
Expand Down Expand Up @@ -52,76 +52,14 @@ const Docs: React.FunctionComponent = () => {

return (
<iframe
// REVISIT: some of this could be moved to the docs repo
// so that it runs earlier to prevent flashing in the UI.
// The load event doesn't run until after the page is fully
// loaded and there doesn't seem to be a reasonable way to
// hook into the iframe to know when it has a new document.
onLoad={(e) => {
// HACK: this mess restores the scroll position when
// the documentation iframe visibility is toggled.
// The iframe will be automatically scrolled to 0 when
// CSS `display: none` is set.

const target = e.target as HTMLIFrameElement;
const contentWindow = target.contentWindow;
if (!contentWindow) {
console.error('could not get iframe content window');
return;
}

// the last "good" scrollY value of the iframe
let iframeScroll = 0;

// This bit monitors the visibility.
// https://stackoverflow.com/a/44670818/1976323
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
// Restore the scroll position when the iframe is
// shown. Toggling the visibility prevents flashing
// the contents from the top of the page before the
// scroll is done.
if (entry.intersectionRatio > 0) {
contentWindow.scrollTo(0, iframeScroll);
contentWindow.document.documentElement.style.visibility =
'visible';
} else {
contentWindow.document.documentElement.style.visibility =
'hidden';
}
});
},
{
root: target.parentElement,
},
);

observer.observe(target);

// Have to remove he observer, otherwise we end up with
// conflicting values when a new page is loaded in the iframe.
contentWindow.addEventListener('unload', () => {
observer.unobserve(target);
});

// And this keeps track of the scroll position.
contentWindow.addEventListener('scroll', () => {
if (contentWindow.scrollY !== 0) {
// Record the current scroll position. If it is 0, it
// could be that the iframe has been hidden or the user
// scrolled there. So we have to ignore 0. But we don't
// want to be one pixel off if the user really did
// scroll there, so we assume that if the last scroll
// is 1, then the user probably went all the way to 0.
if (contentWindow.scrollY === 1) {
iframeScroll = 0;
} else {
iframeScroll = contentWindow.scrollY;
}
}
});

// Override browser default key bindings in iframe.
contentWindow.document.addEventListener('keydown', (e) => {
// use Ctrl-D/Cmd-D to toggle docs
Expand Down
13 changes: 10 additions & 3 deletions src/app/app.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2022 The Pybricks Authors
// Copyright (c) 2020-2026 The Pybricks Authors

// Custom styling for the App control.

Expand Down Expand Up @@ -184,7 +184,14 @@ $dark-splitter-color-hover: color.adjust(
}

// hide the docs and resize separator

// Use visibility rather than display:none so the iframe keeps its layout and
// scroll position when the docs panel is toggled.
// Collapse the secondary pane and splitter to zero width so they don't
// consume space, while keeping the iframe in the DOM.
div.pb-hide-docs > :not(.layout-pane-primary) {
display: none;
visibility: hidden;
pointer-events: none;
flex-basis: 0 !important;
width: 0 !important;
min-width: 0 !important;
}
Loading