diff --git a/package-lock.json b/package-lock.json index 25a1edcb..a544a3c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8361,6 +8361,34 @@ } } }, + "node_modules/@radix-ui/react-form": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.0.tgz", + "integrity": "sha512-1/oVYPDjbFILOLIarcGcMKo+y6SbTVT/iUKVEw59CF4offwZgBgC3ZOeSBewjqU0vdA6FWTPWTN63obj55S/tQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-label": "2.1.0", + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-hover-card": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.1.tgz", @@ -8418,6 +8446,29 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.0.tgz", + "integrity": "sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popover": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.2.tgz", @@ -41158,6 +41209,7 @@ "dependencies": { "@headlessui/react": "^1.7.15", "@heroicons/react": "^2.0.18", + "@radix-ui/react-form": "^0.1.0", "@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-tabs": "^1.1.1", diff --git a/packages/frontmatter/package.json b/packages/frontmatter/package.json index ffbf1a59..e13e719c 100644 --- a/packages/frontmatter/package.json +++ b/packages/frontmatter/package.json @@ -22,6 +22,7 @@ "dependencies": { "@headlessui/react": "^1.7.15", "@heroicons/react": "^2.0.18", + "@radix-ui/react-form": "^0.1.0", "@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-popover": "^1.1.2", "@radix-ui/react-tabs": "^1.1.1", diff --git a/packages/frontmatter/src/FrontmatterBlock.tsx b/packages/frontmatter/src/FrontmatterBlock.tsx index 22a5f441..19344a07 100644 --- a/packages/frontmatter/src/FrontmatterBlock.tsx +++ b/packages/frontmatter/src/FrontmatterBlock.tsx @@ -9,6 +9,7 @@ import { AuthorAndAffiliations, AuthorsList } from './Authors.js'; import * as Popover from '@radix-ui/react-popover'; import { RocketIcon, Cross2Icon } from '@radix-ui/react-icons'; import * as Tabs from '@radix-ui/react-tabs'; +import * as Form from '@radix-ui/react-form'; function ExternalOrInternalLink({ to, @@ -194,6 +195,7 @@ type CommonLaunchProps = { git: string; location: string; ref?: string; + onLaunch?: () => void; }; type JupyterHubLaunchProps = CommonLaunchProps & { @@ -284,43 +286,51 @@ function BinderLaunchContent(props: BinderLaunchProps) { gitComponent = `git/${escapedURL}`; } } - const binderInputRef = useRef(null); - const launchOnBinder = useCallback(() => { - const binderURL = ensureBasename(binderInputRef.current?.value || defaultBinderBaseURL); - binderURL.pathname = `${binderURL.pathname}v2/${gitComponent}/${refComponent}`; - binderURL.search = `?${query}`; + const handleSubmit = useCallback( + (event: React.SyntheticEvent) => { + event.preventDefault(); - window?.open(binderURL, '_blank')?.focus(); - }, [defaultBinderBaseURL, binderInputRef, gitComponent, refComponent, query]); + const data = Object.fromEntries(new FormData(event.currentTarget) as any); + + const binderURL = ensureBasename(data.url || defaultBinderBaseURL); + binderURL.pathname = `${binderURL.pathname}v2/${gitComponent}/${refComponent}`; + binderURL.search = `?${query}`; + + window?.open(binderURL, '_blank')?.focus(); + props.onLaunch?.(); + }, + [defaultBinderBaseURL, gitComponent, refComponent, query], + ); return ( -
+

Launch on a BinderHub e.g. mybinder.org

-
- - -
-
- - - -
-
+ +
+ + Binder URL + + + Please provide a valid URL + +
+ + + +
+ + + + ); } @@ -328,10 +338,6 @@ function JupyterHubLaunchContent(props: JupyterHubLaunchProps) { // Ensure Binder link ends in / const defaultHubBaseURL = props.jupyterhub ?? ''; - // Determine Git ref - // TODO: pull this from frontmatter - const hubInputRef = useRef(null); - const resource = parseKnownGitProvider(props.git); let urlPath = 'lab/tree'; switch (resource?.provider) { @@ -346,48 +352,64 @@ function JupyterHubLaunchContent(props: JupyterHubLaunchProps) { branch: props.ref, // TODO master/main? }); - const launchOnJupyterHub = useCallback(() => { - // Parse input URL (or fallback) - const rawHubBaseURL = hubInputRef.current?.value; - if (!rawHubBaseURL) { - return; - } - const hubURL = ensureBasename(rawHubBaseURL); - hubURL.pathname = `${hubURL.pathname}hub/user-redirect/git-pull`; - hubURL.search = `?${query}`; - - window?.open(hubURL, '_blank')?.focus(); - }, [defaultHubBaseURL, hubInputRef, query]); + const handleSubmit = useCallback( + (event: React.SyntheticEvent) => { + event.preventDefault(); + + const data = Object.fromEntries(new FormData(event.currentTarget) as any); + + // Parse input URL (or fallback) + console.dir(data); + const rawHubBaseURL = data.url; + if (!rawHubBaseURL) { + return; + } + const hubURL = ensureBasename(rawHubBaseURL); + hubURL.pathname = `${hubURL.pathname}hub/user-redirect/git-pull`; + hubURL.search = `?${query}`; + + window?.open(hubURL, '_blank')?.focus(); + props.onLaunch?.(); + }, + [defaultHubBaseURL, query], + ); return ( -
+

Launch on a JupyterHub

-
- - -
-
- - - -
-
+ +
+ JupyterHub URL + + Please enter a URL + + + + Please provide a valid URL + +
+ + + +
+ + + + ); } function LaunchButton(props: BinderLaunchProps | JupyterHubLaunchProps) { + const closeRef = useRef(null); + const closePopover = useCallback(() => { + closeRef.current?.click?.(); + }, []); return ( @@ -409,34 +431,29 @@ function LaunchButton(props: BinderLaunchProps | JupyterHubLaunchProps) { aria-label="Launch into computing interface" > Binder JupyterHub - - + + - - + +