Skip to content

Commit

Permalink
Set title of window from iframe (#183)
Browse files Browse the repository at this point in the history
  • Loading branch information
wch authored Oct 26, 2024
1 parent 741b385 commit 3b4aa74
Showing 4 changed files with 62 additions and 0 deletions.
4 changes: 4 additions & 0 deletions site_template/app/index.html
Original file line number Diff line number Diff line change
@@ -23,6 +23,10 @@
allowCodeUrl: true,
allowGistUrl: true,
allowExampleUrl: true,
setWindowTitle: {
prefix: "",
defaultTitle: document.title,
},
},
"{{APP_ENGINE}}",
);
4 changes: 4 additions & 0 deletions site_template/editor/index.html
Original file line number Diff line number Diff line change
@@ -26,6 +26,10 @@
// viewer embedded in a larger page, it does not make sense to set
// this to true.
updateUrlHashOnRerun: true,
setWindowTitle: {
prefix: "Shiny - ",
defaultTitle: document.title,
},
};

const appRoot = document.getElementById("root");
15 changes: 15 additions & 0 deletions src/Components/App.tsx
Original file line number Diff line number Diff line change
@@ -110,6 +110,17 @@ type AppOptions = {
// When the app is re-run from the Editor, should the URL hash be updated with
// the encoded version of the app?
updateUrlHashOnRerun?: boolean;

// Set window title based on the application iframe's title? If this is
// not false, then the title from the Viewer iframe will be applied to the
// window, with the prefix prepended. If the Viewer iframe's title is empty,
// then the default title will be used instead.
setWindowTitle?:
| {
prefix: string;
defaultTitle: string;
}
| false;
};

export type ProxyHandle = PyodideProxyHandle | WebRProxyHandle;
@@ -420,6 +431,7 @@ export function App({
proxyHandle={proxyHandle}
setViewerMethods={setViewerMethods}
devMode={true}
setWindowTitle={appOptions.setWindowTitle}
/>
</ResizableGrid>
</>
@@ -470,6 +482,7 @@ export function App({
proxyHandle={proxyHandle}
setViewerMethods={setViewerMethods}
devMode={true}
setWindowTitle={appOptions.setWindowTitle}
/>
</ResizableGrid>
</>
@@ -574,6 +587,7 @@ export function App({
proxyHandle={proxyHandle}
setViewerMethods={setViewerMethods}
devMode={true}
setWindowTitle={appOptions.setWindowTitle}
/>
</ResizableGrid>
);
@@ -596,6 +610,7 @@ export function App({
proxyHandle={proxyHandle}
setViewerMethods={setViewerMethods}
devMode={false}
setWindowTitle={appOptions.setWindowTitle}
/>
</div>
</>
39 changes: 39 additions & 0 deletions src/Components/Viewer.tsx
Original file line number Diff line number Diff line change
@@ -127,10 +127,17 @@ export function Viewer({
proxyHandle,
setViewerMethods,
devMode = false,
setWindowTitle = false,
}: {
proxyHandle: ProxyHandle;
setViewerMethods: React.Dispatch<React.SetStateAction<ViewerMethods>>;
devMode?: boolean;
setWindowTitle?:
| {
prefix: string;
defaultTitle: string;
}
| false;
}) {
const viewerFrameRef = React.useRef<HTMLIFrameElement>(null);
const [appRunningState, setAppRunningState] = React.useState<
@@ -141,6 +148,38 @@ export function Viewer({
null,
);

// Add effect to monitor iframe title changes
React.useEffect(() => {
if (!setWindowTitle || !viewerFrameRef.current) return;

const iframe = viewerFrameRef.current;
const observer = new MutationObserver(() => {
if (iframe.contentDocument?.title) {
document.title = setWindowTitle.prefix + iframe.contentDocument.title;
} else {
document.title = setWindowTitle.defaultTitle;
}
});

// Start observing once the iframe loads
const onLoad = () => {
if (iframe.contentDocument) {
observer.observe(iframe.contentDocument, {
subtree: true,
childList: true,
characterData: true,
});
}
};

iframe.addEventListener("load", onLoad);

return () => {
observer.disconnect();
iframe.removeEventListener("load", onLoad);
};
}, [setWindowTitle]);

// Shiny for R
React.useEffect(() => {
if (!proxyHandle.shinyReady) return;

0 comments on commit 3b4aa74

Please sign in to comment.