From dff08ae3e3f36ce4a798d9808756cab45ed36054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Berg=C3=A9?= Date: Thu, 30 Jan 2025 15:01:59 +0100 Subject: [PATCH] Load Scalar only when the button is pressed (#2790) --- .changeset/selfish-badgers-smile.md | 5 ++ .../react-openapi/src/OpenAPICodeSample.tsx | 6 +- .../react-openapi/src/OpenAPIOperation.tsx | 68 +++++++-------- .../react-openapi/src/ScalarApiButton.tsx | 86 +++++++++++++++++-- 4 files changed, 120 insertions(+), 45 deletions(-) create mode 100644 .changeset/selfish-badgers-smile.md diff --git a/.changeset/selfish-badgers-smile.md b/.changeset/selfish-badgers-smile.md new file mode 100644 index 000000000..58a5f9e55 --- /dev/null +++ b/.changeset/selfish-badgers-smile.md @@ -0,0 +1,5 @@ +--- +'@gitbook/react-openapi': patch +--- + +Improve performances by loading Scalar API Client only when the button is clicked diff --git a/packages/react-openapi/src/OpenAPICodeSample.tsx b/packages/react-openapi/src/OpenAPICodeSample.tsx index 5b1118d59..8ed04e303 100644 --- a/packages/react-openapi/src/OpenAPICodeSample.tsx +++ b/packages/react-openapi/src/OpenAPICodeSample.tsx @@ -123,7 +123,11 @@ export function OpenAPICodeSample(props: { tabs={samples} overlay={ data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'] ? null : ( - + ) } /> diff --git a/packages/react-openapi/src/OpenAPIOperation.tsx b/packages/react-openapi/src/OpenAPIOperation.tsx index 618a3cf06..ffb2269d6 100644 --- a/packages/react-openapi/src/OpenAPIOperation.tsx +++ b/packages/react-openapi/src/OpenAPIOperation.tsx @@ -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 ( - -
-
-

- {operation.summary} -

- {operation.description ? ( - - ) : null} -
- - {method.toUpperCase()} - - - - {path} - -
+
+
+

+ {operation.summary} +

+ {operation.description ? ( + + ) : null} +
+ + {method.toUpperCase()} + + + + {path} +
-
-
- -
-
-
- - -
+
+
+
+ +
+
+
+ +
- +
); } diff --git a/packages/react-openapi/src/ScalarApiButton.tsx b/packages/react-openapi/src/ScalarApiButton.tsx index 074fc32fe..bc6650eca 100644 --- a/packages/react-openapi/src/ScalarApiButton.tsx +++ b/packages/react-openapi/src/ScalarApiButton.tsx @@ -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(null); return (
+ + {isOpen && + createPortal( + , + document.body, + )}
); } + +function ScalarModal(props: { + method: string; + path: string; + specUrl: string; + controllerRef: React.Ref; +}) { + return ( + + + + ); +} + +type ScalarModalControllerRef = { + openClient: (() => void) | undefined; +}; + +function ScalarModalController(props: { + method: string; + path: string; + controllerRef: React.Ref; +}) { + 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; +}