Skip to content

Commit 709f1a1

Browse files
amritkmarclaveSamyPesse
authored
Update Scalar Api Client to new version (#2406)
Co-authored-by: marclave <[email protected]> Co-authored-by: Samy Pessé <[email protected]>
1 parent 24b785c commit 709f1a1

File tree

10 files changed

+59
-179
lines changed

10 files changed

+59
-179
lines changed

.changeset/hip-moons-collect.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@gitbook/react-openapi': minor
3+
---
4+
5+
Update Scalar to the latest version, with faster performances and an improved experience

bun.lockb

11 KB
Binary file not shown.

packages/gitbook/src/components/DocumentView/OpenAPI/OpenAPI.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export async function OpenAPI(props: BlockProps<DocumentBlockSwagger>) {
3434

3535
async function OpenAPIBody(props: BlockProps<DocumentBlockSwagger>) {
3636
const { block, context } = props;
37-
const { data, error } = await fetchOpenAPIBlock(block, context.resolveContentRef);
37+
const { data, specUrl, error } = await fetchOpenAPIBlock(block, context.resolveContentRef);
3838

3939
if (error) {
4040
return (
@@ -46,7 +46,7 @@ async function OpenAPIBody(props: BlockProps<DocumentBlockSwagger>) {
4646
);
4747
}
4848

49-
if (!data) {
49+
if (!data || !specUrl) {
5050
return null;
5151
}
5252

@@ -60,6 +60,7 @@ async function OpenAPIBody(props: BlockProps<DocumentBlockSwagger>) {
6060
},
6161
CodeBlock: PlainCodeBlock,
6262
defaultInteractiveOpened: context.mode === 'print',
63+
specUrl,
6364
}}
6465
className="openapi-block"
6566
/>

packages/gitbook/src/components/DocumentView/OpenAPI/scalar.css

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
@import '@scalar/api-client-react/style.css';
2+
13
.light .scalar-modal-layout,
4+
.light .scalar-app,
25
.light .scalar {
36
--scalar-color-1: color-mix(
47
in srgb,
@@ -50,8 +53,13 @@
5053
--scalar-button-1: rgb(49 53 56);
5154
--scalar-button-1-color: #fff;
5255
--scalar-button-1-hover: rgb(28 31 33);
56+
57+
--scalar-shadow-1: 0 1px 3px 0 rgba(0, 0, 0, 0.11);
58+
--scalar-shadow-2: rgba(0, 0, 0, 0.08) 0px 13px 20px 0px, rgba(0, 0, 0, 0.08) 0px 3px 8px 0px,
59+
#eeeeed 0px 0 0 1px;
5360
}
5461
.dark .scalar-modal-layout,
62+
.dark .scalar-app,
5563
.dark .scalar {
5664
--scalar-color-1: color-mix(
5765
in srgb,
@@ -102,8 +110,13 @@
102110
--scalar-button-1: #f6f6f6;
103111
--scalar-button-1-color: #000;
104112
--scalar-button-1-hover: #e7e7e7;
113+
114+
--scalar-shadow-1: 0 1px 3px 0 rgb(0, 0, 0, 0.1);
115+
--scalar-shadow-2: rgba(15, 15, 15, 0.2) 0px 3px 6px, rgba(15, 15, 15, 0.4) 0px 9px 24px,
116+
0 0 0 1px rgba(255, 255, 255, 0.1);
105117
}
106118
.scalar-modal-layout,
119+
.scalar-app,
107120
.scalar {
108121
--scalar-font: initial;
109122
--scalar-font-code: var(--font-mono);
@@ -165,7 +178,7 @@
165178
.scalar-api-client__close:hover {
166179
cursor: pointer;
167180
}
168-
.scalar .scalar-app {
181+
.scalar .scalar-app-layout {
169182
background: var(--scalar-background-3);
170183
height: calc(100dvh - 100px);
171184
max-width: 1280px;
@@ -202,23 +215,6 @@
202215
cursor: pointer;
203216
animation: scalardrawerexitfadein 0.35s forwards;
204217
}
205-
.scalar .scalar-app-exit:before {
206-
content: '\00d7';
207-
font-family: sans-serif;
208-
position: absolute;
209-
top: 0;
210-
right: 0;
211-
font-size: 30px;
212-
font-weight: 100;
213-
line-height: 50px;
214-
right: 12px;
215-
text-align: center;
216-
color: white;
217-
opacity: 0.6;
218-
}
219-
.scalar .scalar-app-exit:hover:before {
220-
opacity: 1;
221-
}
222218
@keyframes scalardrawerexitfadein {
223219
from {
224220
opacity: 0;
@@ -305,11 +301,6 @@
305301
scrollbar-width: thin;
306302
-webkit-overflow-scrolling: touch;
307303
}
308-
@supports (-moz-appearance: none) {
309-
.scalar .custom-scroll {
310-
padding-right: 12px;
311-
}
312-
}
313304
.scalar .custom-scroll:hover {
314305
scrollbar-color: rgba(0, 0, 0, 0.24) transparent;
315306
}
@@ -347,3 +338,18 @@
347338
padding-right: 12px;
348339
}
349340
}
341+
.dark .scalar .client-wrapper-bg-color {
342+
background: linear-gradient(
343+
color-mix(in srgb, var(--tw-bg-base) 6%, transparent) 1%,
344+
color-mix(in srgb, var(--scalar-background-1) 30%, black) 9%
345+
);
346+
}
347+
.light .scalar .client-wrapper-bg-color {
348+
background-color: var(--scalar-background-2) !important;
349+
}
350+
.scalar .gitbook-show {
351+
display: block !important;
352+
}
353+
.scalar .gitbook-hidden {
354+
display: none !important;
355+
}

packages/gitbook/src/lib/openapi.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ export async function fetchOpenAPIBlock(
1919
block: DocumentBlockSwagger,
2020
resolveContentRef: (ref: ContentRef) => Promise<ResolvedContentRef | null>,
2121
): Promise<
22-
| { data: OpenAPIOperationData | null; error?: undefined }
23-
| { error: OpenAPIFetchError; data?: undefined }
22+
| { data: OpenAPIOperationData | null; specUrl: string | null; error?: undefined }
23+
| { error: OpenAPIFetchError; data?: undefined; specUrl?: undefined }
2424
> {
2525
const resolved = block.data.ref ? await resolveContentRef(block.data.ref) : null;
2626
if (!resolved || !block.data.path || !block.data.method) {
27-
return { data: null };
27+
return { data: null, specUrl: null };
2828
}
2929

3030
try {
@@ -37,7 +37,7 @@ export async function fetchOpenAPIBlock(
3737
fetcher,
3838
);
3939

40-
return { data };
40+
return { data, specUrl: resolved.href };
4141
} catch (error) {
4242
if (error instanceof OpenAPIFetchError) {
4343
return { error };

packages/react-openapi/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
},
1010
"version": "0.5.0",
1111
"dependencies": {
12-
"@scalar/api-client-react": "^0.3.7",
13-
"@scalar/oas-utils": "0.1.6",
12+
"@scalar/api-client-react": "1.0.5",
1413
"classnames": "^2.5.1",
1514
"flatted": "^3.2.9",
1615
"openapi-types": "^12.1.3",

packages/react-openapi/src/OpenAPICodeSample.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22

33
import { CodeSampleInput, codeSampleGenerators } from './code-samples';
4-
import { OpenAPIOperationData, toJSON } from './fetchOpenAPIOperation';
4+
import { OpenAPIOperationData } from './fetchOpenAPIOperation';
55
import { generateMediaTypeExample } from './generateSchemaExample';
66
import { InteractiveSection } from './InteractiveSection';
77
import { getServersURL } from './OpenAPIServerURL';
@@ -66,19 +66,14 @@ export function OpenAPICodeSample(props: {
6666
return null;
6767
}
6868

69-
async function fetchOperationData() {
70-
'use server';
71-
return toJSON(data);
72-
}
73-
7469
return (
7570
<InteractiveSection
7671
header="Request"
7772
className="openapi-codesample"
7873
tabs={samples}
7974
overlay={
8075
data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'] ? null : (
81-
<ScalarApiButton fetchOperationData={fetchOperationData} />
76+
<ScalarApiButton />
8277
)
8378
}
8479
/>

packages/react-openapi/src/OpenAPIOperation.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { OpenAPICodeSample } from './OpenAPICodeSample';
77
import { OpenAPIResponseExample } from './OpenAPIResponseExample';
88
import { OpenAPIServerURL } from './OpenAPIServerURL';
99
import { OpenAPISpec } from './OpenAPISpec';
10-
import { ScalarApiClient } from './ScalarApiButton';
1110
import { OpenAPIClientContext, OpenAPIContextProps } from './types';
11+
import { ApiClientModalProvider } from '@scalar/api-client-react';
1212

1313
/**
1414
* Display an interactive OpenAPI operation.
@@ -23,11 +23,15 @@ export function OpenAPIOperation(props: {
2323

2424
const clientContext: OpenAPIClientContext = {
2525
defaultInteractiveOpened: context.defaultInteractiveOpened,
26+
specUrl: context.specUrl,
2627
icons: context.icons,
2728
};
2829

2930
return (
30-
<ScalarApiClient>
31+
<ApiClientModalProvider
32+
configuration={{ spec: { url: context.specUrl } }}
33+
initialRequest={{ path: data.path, method: data.method }}
34+
>
3135
<div className={classNames('openapi-operation', className)}>
3236
<div className="openapi-intro">
3337
<h2 className="openapi-summary">{operation.summary}</h2>
@@ -61,6 +65,6 @@ export function OpenAPIOperation(props: {
6165
</div>
6266
</div>
6367
</div>
64-
</ScalarApiClient>
68+
</ApiClientModalProvider>
6569
);
6670
}
Lines changed: 5 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,17 @@
11
'use client';
2-
import {
3-
Cookie,
4-
getHarRequest,
5-
getParametersFromOperation,
6-
type TransformedOperation,
7-
getRequestFromOperation,
8-
Query,
9-
Header,
10-
RequestBody,
11-
} from '@scalar/oas-utils';
12-
import React from 'react';
13-
14-
import { OpenAPIOperationData, fromJSON } from './fetchOpenAPIOperation';
152

16-
const ApiClientReact = React.lazy(async () => {
17-
const mod = await import('@scalar/api-client-react');
18-
return { default: mod.ApiClientReact };
19-
});
20-
21-
const ScalarContext = React.createContext<
22-
(fetchOperationData: () => Promise<OpenAPIOperationData>) => void
23-
>(() => {});
3+
import { useApiClientModal } from '@scalar/api-client-react';
4+
import React from 'react';
245

256
/**
267
* Button which launches the Scalar API Client
278
*/
28-
export function ScalarApiButton(props: {
29-
fetchOperationData: () => Promise<OpenAPIOperationData>;
30-
}) {
31-
const { fetchOperationData } = props;
32-
const open = React.useContext(ScalarContext);
9+
export function ScalarApiButton() {
10+
const client = useApiClientModal();
3311

3412
return (
3513
<div className="scalar scalar-activate">
36-
<button
37-
className="scalar-activate-button"
38-
onClick={() => {
39-
open(fetchOperationData);
40-
}}
41-
>
14+
<button className="scalar-activate-button" onClick={() => client?.open()}>
4215
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="12" fill="none">
4316
<path
4417
stroke="currentColor"
@@ -51,109 +24,3 @@ export function ScalarApiButton(props: {
5124
</div>
5225
);
5326
}
54-
55-
/**
56-
* Wrap the rendering with a context to open the scalar modal.
57-
*/
58-
export function ScalarApiClient(props: { children: React.ReactNode }) {
59-
const { children } = props;
60-
61-
const [active, setActive] = React.useState<null | {
62-
operationData: OpenAPIOperationData | null;
63-
}>(null);
64-
65-
const proxy = '/~scalar/proxy';
66-
67-
const open = React.useCallback(
68-
async (fetchOperationData: () => Promise<OpenAPIOperationData>) => {
69-
setActive({ operationData: null });
70-
const operationData = fromJSON(await fetchOperationData());
71-
setActive({ operationData });
72-
},
73-
[],
74-
);
75-
76-
const onClose = React.useCallback(() => {
77-
setActive(null);
78-
}, []);
79-
80-
const request = React.useMemo(() => {
81-
const operationData = active?.operationData;
82-
83-
if (!operationData) {
84-
return null;
85-
}
86-
87-
const operationId =
88-
operationData.operation.operationId ?? operationData.method + operationData.path;
89-
90-
const operation = {
91-
...operationData,
92-
httpVerb: operationData.method,
93-
pathParameters: operationData.operation.parameters,
94-
} as TransformedOperation;
95-
96-
const variables = getParametersFromOperation(operation, 'path', false);
97-
98-
const request = getHarRequest(
99-
{
100-
url: operationData.path,
101-
},
102-
getRequestFromOperation(
103-
{
104-
...operation,
105-
information: {
106-
requestBody: operationData.operation.requestBody as RequestBody,
107-
},
108-
},
109-
{ requiredOnly: false },
110-
),
111-
);
112-
113-
return {
114-
id: operationId,
115-
type: operationData.method,
116-
path: operationData.path,
117-
variables,
118-
cookies: request.cookies.map((cookie: Cookie) => {
119-
return { ...cookie, enabled: true };
120-
}),
121-
query: request.queryString.map((queryString: Query) => {
122-
const query: typeof queryString & { required?: boolean } = queryString;
123-
return { ...queryString, enabled: query.required ?? true };
124-
}),
125-
headers: request.headers.map((header: Header) => {
126-
return { ...header, enabled: true };
127-
}),
128-
url: operationData.servers[0]?.url,
129-
body: request.postData?.text,
130-
};
131-
}, [active]);
132-
133-
return (
134-
<ScalarContext.Provider value={open}>
135-
{children}
136-
{active ? (
137-
<div className="scalar">
138-
<div className="scalar-container">
139-
<div className="scalar-app">
140-
<React.Suspense fallback={<ScalarLoading />}>
141-
<ApiClientReact
142-
close={onClose}
143-
proxy={proxy}
144-
isOpen={true}
145-
request={request}
146-
/>
147-
</React.Suspense>
148-
</div>
149-
<div onClick={() => onClose()} className="scalar-app-exit"></div>
150-
</div>
151-
</div>
152-
) : null}
153-
</ScalarContext.Provider>
154-
);
155-
}
156-
157-
function ScalarLoading() {
158-
return <div className="scalar-app-loading">Loading...</div>;
159-
}

0 commit comments

Comments
 (0)