Skip to content

Commit

Permalink
🪝move launch binder logic to reusable hook (#249)
Browse files Browse the repository at this point in the history
* 🪝move launch binder logic to reusable hook
* 🧹 add missing export and fix type
* 📚 changeset
  • Loading branch information
stevejpurves authored Oct 13, 2023
1 parent f23141c commit ab46e84
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 37 deletions.
5 changes: 5 additions & 0 deletions .changeset/warm-mice-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@myst-theme/jupyter': patch
---

Separate out launch binder logic into a reusable hook
38 changes: 5 additions & 33 deletions packages/jupyter/src/controls/Buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import {
import { BoltIcon as BoltIconSolid } from '@heroicons/react/24/solid';
import classNames from 'classnames';
import { Spinner } from './Spinner.js';
import { useThebeServer } from 'thebe-react';
import { useCallback, useState } from 'react';
import { useLaunchBinder } from '../hooks.js';

function BinderButton({
icon,
Expand Down Expand Up @@ -56,19 +55,8 @@ function BinderButton({
}

export function LaunchBinder({ style, location }: { style: 'link' | 'button'; location?: string }) {
const { connect, connecting, ready, server, error } = useThebeServer();
const [autoOpen, setAutoOpen] = useState(false);

// automatically click the link when the server is ready
// but only if the connection was initiated in this component by the user
const autoClick = useCallback(
(node: HTMLAnchorElement) => {
if (node != null && autoOpen) {
node.click();
}
},
[autoOpen],
);
const { connecting, ready, error, autoClickRef, handleStart, getUserServerUrl } =
useLaunchBinder();

let btnStyles =
'flex gap-1 px-2 py-1 font-normal no-underline border rounded bg-slate-200 border-slate-600 hover:bg-slate-800 hover:text-white hover:border-transparent';
Expand All @@ -85,29 +73,13 @@ export function LaunchBinder({ style, location }: { style: 'link' | 'button'; lo
'inline-flex items-center mr-2 font-medium no-underline text-gray-900 lg:mr-0 lg:flex';
}

const handleStart = () => {
if (!connect) {
console.debug("LaunchBinder: Trying to start a connection but connect() isn't defined");
return;
}
setAutoOpen(true);
connect();
};

if (ready) {
// we expect ?token= to be in the url
let userServerUrl = server?.userServerUrl;
if (userServerUrl && location) {
// add the location to the url pathname
const url = new URL(userServerUrl);
if (url.pathname.endsWith('/')) url.pathname = url.pathname.slice(0, -1);
url.pathname = `${url.pathname}/lab/tree${location}`;
userServerUrl = url.toString();
}
const userServerUrl = getUserServerUrl(location);

return (
<a
ref={autoClick}
ref={autoClickRef}
className={btnStyles}
href={userServerUrl}
target="_blank"
Expand Down
52 changes: 51 additions & 1 deletion packages/jupyter/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import type {
MinifiedStreamOutput,
} from 'nbtx';
import { walkOutputs } from 'nbtx';
import { useState, useLayoutEffect } from 'react';
import { useState, useLayoutEffect, useCallback } from 'react';
import { useThebeServer } from 'thebe-react';

interface LongContent {
content_type?: string;
Expand Down Expand Up @@ -136,3 +137,52 @@ export default function useWindowSize() {

return windowSize;
}

export function useLaunchBinder() {
const { connect, connecting, ready, server, error } = useThebeServer();
const [autoOpen, setAutoOpen] = useState(false);

// automatically click the link when the server is ready
// but only if the connection was initiated in this component by the user
const autoClickRef = useCallback(
(node: HTMLAnchorElement) => {
if (node != null && autoOpen) {
node.click();
}
},
[autoOpen],
);

const handleStart = useCallback(() => {
if (!connect) {
console.debug("LaunchBinder: Trying to start a connection but connect() isn't defined");
return;
}
setAutoOpen(true);
connect();
}, [connect]);

const getUserServerUrl = useCallback(
(location?: string) => {
let userServerUrl = server?.userServerUrl;
if (userServerUrl && location) {
// add the location to the url pathname
const url = new URL(userServerUrl);
if (url.pathname.endsWith('/')) url.pathname = url.pathname.slice(0, -1);
url.pathname = `${url.pathname}/lab/tree${location}`;
userServerUrl = url.toString();
}
return userServerUrl;
},
[server],
);

return {
connecting,
ready,
error,
autoClickRef,
handleStart,
getUserServerUrl,
};
}
2 changes: 2 additions & 0 deletions packages/jupyter/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ export * from './ConnectionStatusTray.js';
export * from './providers.js';
export * from './execute/index.js';
export * from './controls/index.js';
export * from './utils.js';
export { useLaunchBinder } from './hooks.js';

export default OUTPUT_RENDERERS;
7 changes: 4 additions & 3 deletions packages/jupyter/src/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { RepoProviderSpec } from 'thebe-core';

function makeThebeOptions(
siteManifest: SiteManifest | undefined,
optionsOverrideFn = (opts?: ExtendedCoreOptions) => opts,
optionsOverrideFn = (opts: ExtendedCoreOptions) => opts,
): {
options?: ExtendedCoreOptions;
githubBadgeUrl?: string;
Expand All @@ -27,7 +27,8 @@ function makeThebeOptions(
binderBadgeUrl,
);

const options = optionsOverrideFn(thebeFrontmatter ? optionsFromFrontmatter : undefined);
let options = optionsFromFrontmatter;
if (options) options = optionsOverrideFn(options);

return {
options,
Expand All @@ -51,7 +52,7 @@ export function ConfiguredThebeServerProvider({
children,
}: React.PropsWithChildren<{
siteManifest?: SiteManifest;
optionOverrideFn?: (opts?: ExtendedCoreOptions) => ExtendedCoreOptions;
optionOverrideFn?: (opts: ExtendedCoreOptions) => ExtendedCoreOptions;
customRepoProviders?: RepoProviderSpec[];
}>) {
const thebe = React.useMemo(
Expand Down

0 comments on commit ab46e84

Please sign in to comment.