Load Scalar only when the button is pressed (#2790)

This commit is contained in:
Greg Bergé
2025-01-30 15:01:59 +01:00
committed by GitHub
parent 8cfa67c1dd
commit dff08ae3e3
4 changed files with 120 additions and 45 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@gitbook/react-openapi': patch
---
Improve performances by loading Scalar API Client only when the button is clicked
@@ -123,7 +123,11 @@ export function OpenAPICodeSample(props: {
tabs={samples} tabs={samples}
overlay={ overlay={
data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'] ? null : ( data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'] ? null : (
<ScalarApiButton method={data.method} path={data.path} /> <ScalarApiButton
method={data.method}
path={data.path}
specUrl={context.specUrl}
/>
) )
} }
/> />
@@ -1,6 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { ApiClientModalProvider } from '@scalar/api-client-react';
import { OpenAPIOperationData } from './fetchOpenAPIOperation'; import { OpenAPIOperationData } from './fetchOpenAPIOperation';
import { Markdown } from './Markdown'; import { Markdown } from './Markdown';
@@ -28,10 +27,6 @@ export function OpenAPIOperation(props: {
}; };
return ( return (
<ApiClientModalProvider
configuration={{ spec: { url: context.specUrl } }}
initialRequest={{ path: data.path, method: data.method }}
>
<div className={classNames('openapi-operation', className)}> <div className={classNames('openapi-operation', className)}>
<div className="openapi-intro"> <div className="openapi-intro">
<h2 className="openapi-summary" id={context.id}> <h2 className="openapi-summary" id={context.id}>
@@ -67,6 +62,5 @@ export function OpenAPIOperation(props: {
</div> </div>
</div> </div>
</div> </div>
</ApiClientModalProvider>
); );
} }
+79 -7
View File
@@ -1,23 +1,33 @@
'use client'; 'use client';
import { useApiClientModal } from '@scalar/api-client-react'; import { ApiClientModalProvider, useApiClientModal } from '@scalar/api-client-react';
import React from 'react'; import React, { useImperativeHandle, useRef } from 'react';
import { createPortal } from 'react-dom';
import { useOpenAPIOperationContext } from './OpenAPIOperationContext'; import { useOpenAPIOperationContext } from './OpenAPIOperationContext';
import { useEventCallback } from 'usehooks-ts';
/** /**
* Button which launches the Scalar API Client * Button which launches the Scalar API Client
*/ */
export function ScalarApiButton({ method, path }: { method: string; path: string }) { export function ScalarApiButton({
const client = useApiClientModal(); method,
const { onOpenClient } = useOpenAPIOperationContext(); path,
specUrl,
}: {
method: string;
path: string;
specUrl: string;
}) {
const [isOpen, setIsOpen] = React.useState(false);
const controllerRef = useRef<ScalarModalControllerRef>(null);
return ( return (
<div className="scalar scalar-activate"> <div className="scalar scalar-activate">
<button <button
className="scalar-activate-button" className="scalar-activate-button"
onClick={() => { onClick={() => {
client?.open({ method, path }); controllerRef.current?.openClient?.();
onOpenClient({ method, path }); setIsOpen(true);
}} }}
> >
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="12" fill="none"> <svg xmlns="http://www.w3.org/2000/svg" width="10" height="12" fill="none">
@@ -29,6 +39,68 @@ export function ScalarApiButton({ method, path }: { method: string; path: string
</svg> </svg>
Test it Test it
</button> </button>
{isOpen &&
createPortal(
<ScalarModal
controllerRef={controllerRef}
method={method}
path={path}
specUrl={specUrl}
/>,
document.body,
)}
</div> </div>
); );
} }
function ScalarModal(props: {
method: string;
path: string;
specUrl: string;
controllerRef: React.Ref<ScalarModalControllerRef>;
}) {
return (
<ApiClientModalProvider
configuration={{ spec: { url: props.specUrl } }}
initialRequest={{ path: props.path, method: props.method }}
>
<ScalarModalController
method={props.method}
path={props.path}
controllerRef={props.controllerRef}
/>
</ApiClientModalProvider>
);
}
type ScalarModalControllerRef = {
openClient: (() => void) | undefined;
};
function ScalarModalController(props: {
method: string;
path: string;
controllerRef: React.Ref<ScalarModalControllerRef>;
}) {
const client = useApiClientModal();
const openClient = client?.open;
useImperativeHandle(
props.controllerRef,
() => ({ openClient: openClient ? () => openClient() : undefined }),
[openClient],
);
// Open the client when the component is mounted.
const { onOpenClient } = useOpenAPIOperationContext();
const trackOpening = useEventCallback(() => {
onOpenClient({ method: props.method, path: props.path });
});
React.useEffect(() => {
if (openClient) {
openClient();
trackOpening();
}
}, [openClient]);
return null;
}