mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-16 23:55:20 +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}
|
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,45 +27,40 @@ export function OpenAPIOperation(props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ApiClientModalProvider
|
<div className={classNames('openapi-operation', className)}>
|
||||||
configuration={{ spec: { url: context.specUrl } }}
|
<div className="openapi-intro">
|
||||||
initialRequest={{ path: data.path, method: data.method }}
|
<h2 className="openapi-summary" id={context.id}>
|
||||||
>
|
{operation.summary}
|
||||||
<div className={classNames('openapi-operation', className)}>
|
</h2>
|
||||||
<div className="openapi-intro">
|
{operation.description ? (
|
||||||
<h2 className="openapi-summary" id={context.id}>
|
<Markdown className="openapi-description" source={operation.description} />
|
||||||
{operation.summary}
|
) : null}
|
||||||
</h2>
|
<div className="openapi-target">
|
||||||
{operation.description ? (
|
<span
|
||||||
<Markdown className="openapi-description" source={operation.description} />
|
className={classNames(
|
||||||
) : null}
|
'openapi-method',
|
||||||
<div className="openapi-target">
|
`openapi-method-${method.toLowerCase()}`,
|
||||||
<span
|
)}
|
||||||
className={classNames(
|
>
|
||||||
'openapi-method',
|
{method.toUpperCase()}
|
||||||
`openapi-method-${method.toLowerCase()}`,
|
</span>
|
||||||
)}
|
<span className="openapi-url">
|
||||||
>
|
<OpenAPIServerURL servers={servers} />
|
||||||
{method.toUpperCase()}
|
{path}
|
||||||
</span>
|
</span>
|
||||||
<span className="openapi-url">
|
|
||||||
<OpenAPIServerURL servers={servers} />
|
|
||||||
{path}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={classNames('openapi-columns')}>
|
</div>
|
||||||
<div className={classNames('openapi-column-spec')}>
|
<div className={classNames('openapi-columns')}>
|
||||||
<OpenAPISpec data={data} context={clientContext} />
|
<div className={classNames('openapi-column-spec')}>
|
||||||
</div>
|
<OpenAPISpec data={data} context={clientContext} />
|
||||||
<div className={classNames('openapi-column-preview')}>
|
</div>
|
||||||
<div className={classNames('openapi-column-preview-body')}>
|
<div className={classNames('openapi-column-preview')}>
|
||||||
<OpenAPICodeSample {...props} />
|
<div className={classNames('openapi-column-preview-body')}>
|
||||||
<OpenAPIResponseExample {...props} />
|
<OpenAPICodeSample {...props} />
|
||||||
</div>
|
<OpenAPIResponseExample {...props} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ApiClientModalProvider>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user