mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-12 05:48:57 +00:00
Load Scalar only when the button is pressed (#2790)
This commit is contained in:
@@ -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}
|
||||
overlay={
|
||||
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 classNames from 'classnames';
|
||||
import { ApiClientModalProvider } from '@scalar/api-client-react';
|
||||
|
||||
import { OpenAPIOperationData } from './fetchOpenAPIOperation';
|
||||
import { Markdown } from './Markdown';
|
||||
@@ -28,45 +27,40 @@ export function OpenAPIOperation(props: {
|
||||
};
|
||||
|
||||
return (
|
||||
<ApiClientModalProvider
|
||||
configuration={{ spec: { url: context.specUrl } }}
|
||||
initialRequest={{ path: data.path, method: data.method }}
|
||||
>
|
||||
<div className={classNames('openapi-operation', className)}>
|
||||
<div className="openapi-intro">
|
||||
<h2 className="openapi-summary" id={context.id}>
|
||||
{operation.summary}
|
||||
</h2>
|
||||
{operation.description ? (
|
||||
<Markdown className="openapi-description" source={operation.description} />
|
||||
) : null}
|
||||
<div className="openapi-target">
|
||||
<span
|
||||
className={classNames(
|
||||
'openapi-method',
|
||||
`openapi-method-${method.toLowerCase()}`,
|
||||
)}
|
||||
>
|
||||
{method.toUpperCase()}
|
||||
</span>
|
||||
<span className="openapi-url">
|
||||
<OpenAPIServerURL servers={servers} />
|
||||
{path}
|
||||
</span>
|
||||
</div>
|
||||
<div className={classNames('openapi-operation', className)}>
|
||||
<div className="openapi-intro">
|
||||
<h2 className="openapi-summary" id={context.id}>
|
||||
{operation.summary}
|
||||
</h2>
|
||||
{operation.description ? (
|
||||
<Markdown className="openapi-description" source={operation.description} />
|
||||
) : null}
|
||||
<div className="openapi-target">
|
||||
<span
|
||||
className={classNames(
|
||||
'openapi-method',
|
||||
`openapi-method-${method.toLowerCase()}`,
|
||||
)}
|
||||
>
|
||||
{method.toUpperCase()}
|
||||
</span>
|
||||
<span className="openapi-url">
|
||||
<OpenAPIServerURL servers={servers} />
|
||||
{path}
|
||||
</span>
|
||||
</div>
|
||||
<div className={classNames('openapi-columns')}>
|
||||
<div className={classNames('openapi-column-spec')}>
|
||||
<OpenAPISpec data={data} context={clientContext} />
|
||||
</div>
|
||||
<div className={classNames('openapi-column-preview')}>
|
||||
<div className={classNames('openapi-column-preview-body')}>
|
||||
<OpenAPICodeSample {...props} />
|
||||
<OpenAPIResponseExample {...props} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={classNames('openapi-columns')}>
|
||||
<div className={classNames('openapi-column-spec')}>
|
||||
<OpenAPISpec data={data} context={clientContext} />
|
||||
</div>
|
||||
<div className={classNames('openapi-column-preview')}>
|
||||
<div className={classNames('openapi-column-preview-body')}>
|
||||
<OpenAPICodeSample {...props} />
|
||||
<OpenAPIResponseExample {...props} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ApiClientModalProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,33 @@
|
||||
'use client';
|
||||
|
||||
import { useApiClientModal } from '@scalar/api-client-react';
|
||||
import React from 'react';
|
||||
import { ApiClientModalProvider, useApiClientModal } from '@scalar/api-client-react';
|
||||
import React, { useImperativeHandle, useRef } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import { useOpenAPIOperationContext } from './OpenAPIOperationContext';
|
||||
import { useEventCallback } from 'usehooks-ts';
|
||||
|
||||
/**
|
||||
* Button which launches the Scalar API Client
|
||||
*/
|
||||
export function ScalarApiButton({ method, path }: { method: string; path: string }) {
|
||||
const client = useApiClientModal();
|
||||
const { onOpenClient } = useOpenAPIOperationContext();
|
||||
export function ScalarApiButton({
|
||||
method,
|
||||
path,
|
||||
specUrl,
|
||||
}: {
|
||||
method: string;
|
||||
path: string;
|
||||
specUrl: string;
|
||||
}) {
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const controllerRef = useRef<ScalarModalControllerRef>(null);
|
||||
return (
|
||||
<div className="scalar scalar-activate">
|
||||
<button
|
||||
className="scalar-activate-button"
|
||||
onClick={() => {
|
||||
client?.open({ method, path });
|
||||
onOpenClient({ method, path });
|
||||
controllerRef.current?.openClient?.();
|
||||
setIsOpen(true);
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
Test it
|
||||
</button>
|
||||
|
||||
{isOpen &&
|
||||
createPortal(
|
||||
<ScalarModal
|
||||
controllerRef={controllerRef}
|
||||
method={method}
|
||||
path={path}
|
||||
specUrl={specUrl}
|
||||
/>,
|
||||
document.body,
|
||||
)}
|
||||
</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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user