Skip to content

Commit

Permalink
feat: use form
Browse files Browse the repository at this point in the history
  • Loading branch information
agoose77 committed Nov 29, 2024
1 parent d46817e commit d35cb27
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 81 deletions.
52 changes: 52 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/frontmatter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
179 changes: 98 additions & 81 deletions packages/frontmatter/src/FrontmatterBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -194,6 +195,7 @@ type CommonLaunchProps = {
git: string;
location: string;
ref?: string;
onLaunch?: () => void;
};

type JupyterHubLaunchProps = CommonLaunchProps & {
Expand Down Expand Up @@ -284,54 +286,58 @@ function BinderLaunchContent(props: BinderLaunchProps) {
gitComponent = `git/${escapedURL}`;
}
}
const binderInputRef = useRef<HTMLInputElement>(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<HTMLFormElement>) => {
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 (
<div className="flex flex-col gap-2.5">
<Form.Root className="w-[260px]" onSubmit={handleSubmit}>
<p className="mb-2.5 text-[15px] font-medium leading-[19px]">
Launch on a BinderHub e.g. <a href="https://mybinder.org">mybinder.org</a>
</p>
<fieldset className="flex items-center gap-5">
<label className="w-[75px] text-[13px]" htmlFor="width">
Binder URL
</label>
<input
ref={binderInputRef}
className="inline-flex h-[25px] w-full flex-1 items-center justify-center rounded px-2.5 text-[13px] leading-none bg-gray-50 dark:bg-gray-700"
id="width"
placeholder={defaultBinderBaseURL}
/>
</fieldset>
<div className="mt-[25px] flex justify-end">
<Popover.Close asChild>
<button
className="inline-flex h-[35px] items-center justify-center rounded bg-green4 px-[15px] font-medium leading-none bg-orange-500 outline-none text-white"
onClick={launchOnBinder}
>
Launch
</button>
</Popover.Close>
</div>
</div>
<Form.Field className="mb-2.5 grid" name="url">
<div className="flex items-baseline justify-between">
<Form.Label className="text-[15px] font-medium leading-[35px] text-white">
Binder URL
</Form.Label>
<Form.Message className="text-[13px] text-white opacity-80" match="typeMismatch">
Please provide a valid URL
</Form.Message>
</div>
<Form.Control asChild>
<input
className="box-border inline-flex h-[35px] w-full appearance-none items-center justify-center rounded px-2.5 text-[15px] leading-none shadow-[0_0_0_1px] shadow-slate-400 outline-none bg-gray-50 dark:bg-gray-700 hover:shadow-[0_0_0_1px_black] focus:shadow-[0_0_0_2px_black]"
type="url"
placeholder={defaultBinderBaseURL}
/>
</Form.Control>
</Form.Field>
<Form.Submit asChild>
<button className="inline-flex h-[35px] items-center justify-center rounded bg-green4 px-[15px] font-medium leading-none bg-orange-500 hover:bg-orange-600 outline-none text-white focus:shadow-[0_0_0_2px] focus:shadow-black focus:outline-none">
Launch
</button>
</Form.Submit>
</Form.Root>
);
}

function JupyterHubLaunchContent(props: JupyterHubLaunchProps) {
// Ensure Binder link ends in /
const defaultHubBaseURL = props.jupyterhub ?? '';

// Determine Git ref
// TODO: pull this from frontmatter
const hubInputRef = useRef<HTMLInputElement>(null);

const resource = parseKnownGitProvider(props.git);
let urlPath = 'lab/tree';
switch (resource?.provider) {
Expand All @@ -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<HTMLFormElement>) => {
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 (
<div className="flex flex-col gap-2.5">
<Form.Root className="w-[260px]" onSubmit={handleSubmit}>
<p className="mb-2.5 text-[15px] font-medium leading-[19px]">Launch on a JupyterHub</p>
<fieldset className="flex items-center gap-5">
<label className="w-[75px] text-[13px]" htmlFor="width">
JupyterHub URL
</label>
<input
ref={hubInputRef}
className="inline-flex h-[25px] w-full flex-1 items-center justify-center rounded px-2.5 text-[13px] leading-none bg-gray-50 dark:bg-gray-700"
id="width"
placeholder={defaultHubBaseURL}
/>
</fieldset>
<div className="mt-[25px] flex justify-end">
<Popover.Close asChild>
<button
className="inline-flex h-[35px] items-center justify-center rounded bg-green4 px-[15px] font-medium leading-none bg-orange-500 outline-none text-white"
onClick={launchOnJupyterHub}
>
Launch
</button>
</Popover.Close>
</div>
</div>
<Form.Field className="mb-2.5 grid" name="url">
<div className="flex items-baseline justify-between">
<Form.Label className="text-[15px] font-medium leading-[35px]">JupyterHub URL</Form.Label>
<Form.Message className="text-[13px] text-white opacity-80" match="valueMissing">
Please enter a URL
</Form.Message>

<Form.Message className="text-[13px] opacity-80" match="typeMismatch">
Please provide a valid URL
</Form.Message>
</div>
<Form.Control asChild>
<input
className="box-border inline-flex h-[35px] w-full appearance-none items-center justify-center rounded px-2.5 text-[15px] leading-none shadow-[0_0_0_1px] shadow-slate-400 outline-none bg-gray-50 dark:bg-gray-700 hover:shadow-[0_0_0_1px_black] focus:shadow-[0_0_0_2px_black]"
type="url"
required
/>
</Form.Control>
</Form.Field>
<Form.Submit asChild>
<button className="inline-flex h-[35px] items-center justify-center rounded bg-green4 px-[15px] font-medium leading-none bg-orange-500 hover:bg-orange-600 outline-none text-white focus:shadow-[0_0_0_2px] focus:shadow-black focus:outline-none">
Launch
</button>
</Form.Submit>
</Form.Root>
);
}

function LaunchButton(props: BinderLaunchProps | JupyterHubLaunchProps) {
const closeRef = useRef<HTMLButtonElement>(null);
const closePopover = useCallback(() => {
closeRef.current?.click?.();
}, []);
return (
<Popover.Root>
<Popover.Trigger asChild>
Expand All @@ -409,34 +431,29 @@ function LaunchButton(props: BinderLaunchProps | JupyterHubLaunchProps) {
aria-label="Launch into computing interface"
>
<Tabs.Trigger
className="flex h-[45px] flex-1 cursor-default items-center justify-center bg-white px-5 text-[15px] outline-none data-[state=active]:underline"
className="flex h-[45px] flex-1 cursor-default items-center justify-center px-5 text-[15px] outline-none data-[state=active]:underline"
value="binder"
>
Binder
</Tabs.Trigger>
<Tabs.Trigger
className="flex h-[45px] flex-1 cursor-default items-center justify-center bg-white px-5 text-[15px] outline-none data-[state=active]:underline"
className="flex h-[45px] flex-1 cursor-default items-center justify-center px-5 text-[15px] outline-none data-[state=active]:underline"
value="jupyterhub"
>
JupyterHub
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content
className="grow rounded-b-md bg-white p-5 outline-none focus:shadow-[0_0_0_2px] focus:shadow-black"
value="binder"
>
<BinderLaunchContent {...props} />
<Tabs.Content className="grow rounded-b-md p-5 outline-none" value="binder">
<BinderLaunchContent {...props} onLaunch={closePopover} />
</Tabs.Content>
<Tabs.Content
className="grow rounded-b-md bg-white p-5 outline-none focus:shadow-[0_0_0_2px] focus:shadow-black"
value="jupyterhub"
>
<JupyterHubLaunchContent {...props} />
<Tabs.Content className="grow rounded-b-md p-5 outline-none" value="jupyterhub">
<JupyterHubLaunchContent {...props} onLaunch={closePopover} />
</Tabs.Content>
</Tabs.Root>
<Popover.Close
className="absolute right-[5px] top-[5px] inline-flex size-[25px] items-center justify-center rounded-full"
aria-label="Close"
ref={closeRef}
>
<Cross2Icon />
</Popover.Close>
Expand Down

0 comments on commit d35cb27

Please sign in to comment.