fumadocs-openapi 6.1.0 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/playground/auth/oauth-dialog.d.ts +10 -0
- package/dist/playground/auth/oauth-dialog.d.ts.map +1 -0
- package/dist/playground/auth/oauth-dialog.js +90 -0
- package/dist/playground/client.d.ts.map +1 -1
- package/dist/playground/client.js +62 -54
- package/dist/playground/index.js +12 -2
- package/dist/render/codeblock.js +1 -1
- package/dist/ui/components/dialog.d.ts +20 -0
- package/dist/ui/components/dialog.d.ts.map +1 -0
- package/dist/ui/components/dialog.js +23 -0
- package/dist/ui/components/input.js +1 -1
- package/dist/utils/use-query.d.ts +3 -2
- package/dist/utils/use-query.d.ts.map +1 -1
- package/dist/utils/use-query.js +12 -5
- package/package.json +4 -3
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { OpenAPIV3_1 } from 'openapi-types';
|
|
2
|
+
export interface AuthDialogProps {
|
|
3
|
+
flow: keyof OpenAPIV3_1.OAuth2SecurityScheme['flows'];
|
|
4
|
+
scheme: OpenAPIV3_1.OAuth2SecurityScheme;
|
|
5
|
+
setToken: (token: string) => void;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
export declare function OauthDialog({ flow, scheme, setToken, children, }: AuthDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare const OauthDialogTrigger: import("react").ForwardRefExoticComponent<import("@radix-ui/react-dialog").DialogTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
10
|
+
//# sourceMappingURL=oauth-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-dialog.d.ts","sourceRoot":"","sources":["../../../src/playground/auth/oauth-dialog.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAQjD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,WAAW,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,EAAE,WAAW,CAAC,oBAAoB,CAAC;IAEzC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAaD,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,QAAQ,GACT,EAAE,eAAe,2CAkKjB;AAED,eAAO,MAAM,kBAAkB,mJAAgB,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from '../../ui/components/dialog.js';
|
|
3
|
+
import { useForm } from 'react-hook-form';
|
|
4
|
+
import { Input, labelVariants } from '../../ui/components/input.js';
|
|
5
|
+
import { useQuery } from '../../utils/use-query.js';
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
7
|
+
import { cn } from 'fumadocs-ui/components/api';
|
|
8
|
+
import { buttonVariants } from 'fumadocs-ui/components/ui/button';
|
|
9
|
+
export function OauthDialog({ flow, scheme, setToken, children, }) {
|
|
10
|
+
const [open, setOpen] = useState(false);
|
|
11
|
+
const form = useForm({
|
|
12
|
+
defaultValues: {
|
|
13
|
+
clientId: '',
|
|
14
|
+
clientSecret: '',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
const tokenQuery = useQuery(async (code, state) => {
|
|
18
|
+
const value = scheme.flows.authorizationCode;
|
|
19
|
+
const res = await fetch(value.tokenUrl, {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
23
|
+
},
|
|
24
|
+
body: new URLSearchParams({
|
|
25
|
+
grant_type: 'authorization_code',
|
|
26
|
+
code,
|
|
27
|
+
// note: `state` could be invalid, but server will check it
|
|
28
|
+
redirect_uri: state.redirect_uri,
|
|
29
|
+
client_id: state.client_id,
|
|
30
|
+
client_secret: state.client_secret,
|
|
31
|
+
}).toString(),
|
|
32
|
+
});
|
|
33
|
+
if (!res.ok)
|
|
34
|
+
throw new Error(await res.text());
|
|
35
|
+
const { access_token } = (await res.json());
|
|
36
|
+
setToken(access_token);
|
|
37
|
+
setOpen(false);
|
|
38
|
+
});
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const params = new URLSearchParams(window.location.search);
|
|
41
|
+
const code = params.get('code');
|
|
42
|
+
const state = params.get('state');
|
|
43
|
+
if (!code || !state || flow !== 'authorizationCode')
|
|
44
|
+
return;
|
|
45
|
+
const parsedState = JSON.parse(state);
|
|
46
|
+
setOpen(true);
|
|
47
|
+
form.setValue('clientId', parsedState.client_id);
|
|
48
|
+
form.setValue('clientSecret', parsedState.client_secret);
|
|
49
|
+
tokenQuery.start(code, parsedState);
|
|
50
|
+
window.history.replaceState(null, '', window.location.pathname);
|
|
51
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- first page load only
|
|
52
|
+
}, []);
|
|
53
|
+
const authorize = useQuery(async (values) => {
|
|
54
|
+
if (flow === 'authorizationCode') {
|
|
55
|
+
const value = scheme.flows[flow];
|
|
56
|
+
const params = new URLSearchParams();
|
|
57
|
+
params.set('response_type', 'code');
|
|
58
|
+
params.set('client_id', values.clientId);
|
|
59
|
+
params.set('redirect_uri', window.location.href);
|
|
60
|
+
params.set('scope', Object.keys(value.scopes).join('+'));
|
|
61
|
+
params.set('state', JSON.stringify({
|
|
62
|
+
client_id: values.clientId,
|
|
63
|
+
client_secret: values.clientSecret,
|
|
64
|
+
redirect_uri: window.location.href,
|
|
65
|
+
}));
|
|
66
|
+
window.location.replace(`${value.authorizationUrl}?${params.toString()}`);
|
|
67
|
+
}
|
|
68
|
+
if (flow === 'clientCredentials') {
|
|
69
|
+
const value = scheme.flows[flow];
|
|
70
|
+
await fetch(value.tokenUrl, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: {
|
|
73
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
74
|
+
},
|
|
75
|
+
body: new URLSearchParams({
|
|
76
|
+
grant_type: 'client_credentials',
|
|
77
|
+
scope: Object.keys(value.scopes).join('+'),
|
|
78
|
+
}).toString(),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
const onSubmit = form.handleSubmit((values) => {
|
|
83
|
+
return authorize.start(values);
|
|
84
|
+
});
|
|
85
|
+
const isLoading = authorize.isLoading || tokenQuery.isLoading;
|
|
86
|
+
return (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [children, _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Authorization" }), _jsx(DialogDescription, { children: "Obtain the access token for API." })] }), _jsxs("form", { className: "flex flex-col gap-6", onSubmit: onSubmit, children: [(flow === 'authorizationCode' || flow === 'clientCredentials') && (_jsxs(_Fragment, { children: [_jsxs("fieldset", { className: "flex flex-col gap-1.5", children: [_jsx("label", { htmlFor: "client_id", className: cn(labelVariants()), children: "Client ID" }), _jsx("p", { className: "text-fd-muted-foreground text-sm", children: "The client ID of your OAuth application." }), _jsx(Input, { id: "client_id", placeholder: "Enter value", type: "text", disabled: isLoading, ...form.register('clientId', { required: true }) })] }), _jsxs("fieldset", { className: "flex flex-col gap-1.5", children: [_jsx("label", { htmlFor: "client_secret", className: cn(labelVariants()), children: "Client Secret" }), _jsx("p", { className: "text-fd-muted-foreground text-sm", children: "The client secret of your OAuth application." }), _jsx(Input, { id: "client_secret", placeholder: "Enter value", type: "password", disabled: isLoading, ...form.register('clientSecret', { required: true }) })] })] })), tokenQuery.error ? (_jsx("p", { className: "text-red-400 font-medium text-sm", children: String(tokenQuery.error) })) : null, _jsx("button", { disabled: isLoading, className: cn(buttonVariants({
|
|
87
|
+
color: 'primary',
|
|
88
|
+
})), children: tokenQuery.isLoading ? 'Fetching token...' : 'Submit' })] })] })] }));
|
|
89
|
+
}
|
|
90
|
+
export const OauthDialogTrigger = DialogTrigger;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/playground/client.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,cAAc,EAInB,KAAK,EAAE,EAGP,KAAK,SAAS,EAGf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EACV,SAAS,EACT,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGxD,OAAO,KAAK,EACV,qBAAqB,EACrB,eAAe,EACf,aAAa,EACd,MAAM,oBAAoB,CAAC;AAa5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/playground/client.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useMemo, useRef, useEffect, Fragment, createContext, useContext, } from 'react';
|
|
4
4
|
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
|
5
5
|
import { cn, buttonVariants } from 'fumadocs-ui/components/api';
|
|
@@ -14,6 +14,7 @@ import { useQuery } from '../utils/use-query.js';
|
|
|
14
14
|
import ServerSelect from '../ui/server-select.js';
|
|
15
15
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from 'fumadocs-ui/components/ui/collapsible';
|
|
16
16
|
import { ChevronDown } from 'lucide-react';
|
|
17
|
+
import { OauthDialog, OauthDialogTrigger, } from '../playground/auth/oauth-dialog.js';
|
|
17
18
|
const SchemaContext = createContext(undefined);
|
|
18
19
|
export function useSchemaContext() {
|
|
19
20
|
const ctx = useContext(SchemaContext);
|
|
@@ -48,36 +49,34 @@ export function Client({ route, method = 'GET', authorization, path = [], header
|
|
|
48
49
|
const fetcher = await import('./fetcher.js').then((mod) => mod.createBrowserFetcher(body, schemas));
|
|
49
50
|
const query = { ...input.query };
|
|
50
51
|
const header = { ...input.header };
|
|
51
|
-
if (
|
|
52
|
-
if (authorization.
|
|
53
|
-
|
|
54
|
-
header[authorization.name] = input.authorization;
|
|
55
|
-
}
|
|
56
|
-
else if (authorization.in === 'query') {
|
|
57
|
-
query[authorization.name] = input.authorization;
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
if ('cookie' in header) {
|
|
61
|
-
header.Cookie = header.cookie;
|
|
62
|
-
delete header.cookie;
|
|
63
|
-
}
|
|
64
|
-
header.Cookie = [
|
|
65
|
-
header.Cookie,
|
|
66
|
-
`${authorization.name}=${input.authorization}`,
|
|
67
|
-
]
|
|
68
|
-
.filter((s) => s.length > 0)
|
|
69
|
-
.join('; ');
|
|
70
|
-
}
|
|
52
|
+
if (authorization?.type === 'apiKey') {
|
|
53
|
+
if (authorization.in === 'header') {
|
|
54
|
+
header[authorization.name] = input.authorization;
|
|
71
55
|
}
|
|
72
|
-
else if (authorization.
|
|
73
|
-
authorization.
|
|
74
|
-
if (typeof input.authorization === 'object')
|
|
75
|
-
header.Authorization = `Basic ${btoa(`${input.authorization.username}:${input.authorization.password}`)}`;
|
|
56
|
+
else if (authorization.in === 'query') {
|
|
57
|
+
query[authorization.name] = input.authorization;
|
|
76
58
|
}
|
|
77
59
|
else {
|
|
78
|
-
header
|
|
60
|
+
if ('cookie' in header) {
|
|
61
|
+
header.Cookie = header.cookie;
|
|
62
|
+
delete header.cookie;
|
|
63
|
+
}
|
|
64
|
+
header.Cookie = [
|
|
65
|
+
header.Cookie,
|
|
66
|
+
`${authorization.name}=${input.authorization}`,
|
|
67
|
+
]
|
|
68
|
+
.filter((s) => s.length > 0)
|
|
69
|
+
.join('; ');
|
|
79
70
|
}
|
|
80
71
|
}
|
|
72
|
+
else if (authorization?.type === 'http' &&
|
|
73
|
+
authorization.scheme === 'basic') {
|
|
74
|
+
if (typeof input.authorization === 'object')
|
|
75
|
+
header.Authorization = `Basic ${btoa(`${input.authorization.username}:${input.authorization.password}`)}`;
|
|
76
|
+
}
|
|
77
|
+
else if (authorization) {
|
|
78
|
+
header.Authorization = input.authorization;
|
|
79
|
+
}
|
|
81
80
|
const serverUrl = serverRef.current
|
|
82
81
|
? getUrl(serverRef.current.url, serverRef.current.variables)
|
|
83
82
|
: window.location.origin;
|
|
@@ -126,34 +125,43 @@ export function Client({ route, method = 'GET', authorization, path = [], header
|
|
|
126
125
|
}
|
|
127
126
|
return (_jsx(FieldSet, { name: info.name, fieldName: fieldName, field: info }, key));
|
|
128
127
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
128
|
+
let authField = null;
|
|
129
|
+
if (authorization) {
|
|
130
|
+
authField = (_jsxs(_Fragment, { children: [renderCustomField('authorization', authorization?.type === 'http' && authorization.scheme === 'basic'
|
|
131
|
+
? {
|
|
132
|
+
name: 'Authorization',
|
|
133
|
+
type: 'object',
|
|
134
|
+
isRequired: true,
|
|
135
|
+
properties: {
|
|
136
|
+
username: {
|
|
137
|
+
type: 'string',
|
|
138
|
+
isRequired: true,
|
|
139
|
+
defaultValue: '',
|
|
140
|
+
},
|
|
141
|
+
password: {
|
|
142
|
+
type: 'string',
|
|
143
|
+
isRequired: true,
|
|
144
|
+
defaultValue: '',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
}
|
|
148
|
+
: {
|
|
149
|
+
name: 'Authorization',
|
|
150
|
+
type: 'string',
|
|
151
|
+
isRequired: true,
|
|
152
|
+
description: 'The Authorization access token',
|
|
153
|
+
defaultValue: '',
|
|
154
|
+
}, fields.auth), authorization?.type === 'oauth2' && (_jsx(OauthDialogTrigger, { type: "button", className: cn(buttonVariants({
|
|
155
|
+
color: 'secondary',
|
|
156
|
+
})), children: "Open" }))] }));
|
|
157
|
+
}
|
|
158
|
+
let children = (_jsxs("form", { ...props, className: cn('not-prose flex flex-col gap-2 rounded-xl border p-3 shadow-md', props.className), onSubmit: onSubmit, children: [_jsx(FormHeader, { method: method, route: route, isLoading: testQuery.isLoading }), servers.length > 1 ? (_jsx(CollapsiblePanel, { title: "Server URL", children: _jsx(ServerSelect, {}) })) : null, header.length > 0 || authorization ? (_jsxs(CollapsiblePanel, { title: "Headers", children: [authField, header.map((field) => renderCustomField(`header.${field.name}`, field, fields.header, field.name))] })) : null, path.length > 0 ? (_jsx(CollapsiblePanel, { title: "Path", children: path.map((field) => renderCustomField(`path.${field.name}`, field, fields.path, field.name)) })) : null, query.length > 0 ? (_jsx(CollapsiblePanel, { title: "Query", children: query.map((field) => renderCustomField(`query.${field.name}`, field, fields.query, field.name)) })) : null, body ? (_jsx(CollapsiblePanel, { title: "Body", children: body.type === 'object' && !fields.body ? (_jsx(ObjectInput, { field: body, fieldName: "body" })) : (renderCustomField('body', body, fields.body)) })) : null, testQuery.data ? _jsx(ResultDisplay, { data: testQuery.data }) : null] }));
|
|
159
|
+
if (authorization?.type === 'oauth2') {
|
|
160
|
+
// only the first one, so it looks simpler :)
|
|
161
|
+
const flow = Object.keys(authorization.flows)[0];
|
|
162
|
+
children = (_jsx(OauthDialog, { flow: flow, scheme: authorization, setToken: (token) => form.setValue('authorization', `Bearer ${token}`), children: children }));
|
|
163
|
+
}
|
|
164
|
+
return (_jsx(FormProvider, { ...form, children: _jsx(SchemaContext.Provider, { value: useMemo(() => ({ references: schemas, dynamic: dynamicRef }), [schemas]), children: children }) }));
|
|
157
165
|
}
|
|
158
166
|
function createPathnameFromInput(route, path, query) {
|
|
159
167
|
let pathname = route;
|
package/dist/playground/index.js
CHANGED
|
@@ -48,9 +48,19 @@ function getAuthorizationField(method, { schema: { document } }) {
|
|
|
48
48
|
if (security.length === 0)
|
|
49
49
|
return;
|
|
50
50
|
const singular = security.find((requirements) => Object.keys(requirements).length === 1);
|
|
51
|
-
if (!singular)
|
|
51
|
+
if (!singular) {
|
|
52
|
+
console.warn(`Cannot find suitable security schema for API Playground from ${JSON.stringify(security, null, 2)}. Only one requirement is allowed`);
|
|
52
53
|
return;
|
|
53
|
-
|
|
54
|
+
}
|
|
55
|
+
const scheme = getSecurities(singular, document)[0];
|
|
56
|
+
if (scheme.type === 'oauth2') {
|
|
57
|
+
const flow = Object.keys(scheme.flows).at(0);
|
|
58
|
+
if (!flow)
|
|
59
|
+
throw new Error("security scheme's `flows` must not be empty");
|
|
60
|
+
if (flow === 'implicit' || flow === 'password')
|
|
61
|
+
throw new Error(`OAuth 2.0 flow type: ${flow} is not supported, consider other types like \`authorizationCode\` instead.`);
|
|
62
|
+
}
|
|
63
|
+
return scheme;
|
|
54
64
|
}
|
|
55
65
|
function getIdFromSchema(schema, required, ctx) {
|
|
56
66
|
const registered = ctx.registered.get(schema);
|
package/dist/render/codeblock.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as Base from 'fumadocs-ui/components/codeblock';
|
|
3
|
-
import { highlight } from 'fumadocs-core/
|
|
3
|
+
import { highlight } from 'fumadocs-core/highlight';
|
|
4
4
|
export async function CodeBlock({ code, lang, options, ...rest }) {
|
|
5
5
|
const rendered = await highlight(code, {
|
|
6
6
|
lang,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
3
|
+
declare const Dialog: React.FC<DialogPrimitive.DialogProps>;
|
|
4
|
+
declare const DialogTrigger: React.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
5
|
+
declare const DialogPortal: React.FC<DialogPrimitive.DialogPortalProps>;
|
|
6
|
+
declare const DialogClose: React.ForwardRefExoticComponent<DialogPrimitive.DialogCloseProps & React.RefAttributes<HTMLButtonElement>>;
|
|
7
|
+
declare const DialogOverlay: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogOverlayProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
8
|
+
declare const DialogContent: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
9
|
+
declare const DialogHeader: {
|
|
10
|
+
({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
displayName: string;
|
|
12
|
+
};
|
|
13
|
+
declare const DialogFooter: {
|
|
14
|
+
({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
displayName: string;
|
|
16
|
+
};
|
|
17
|
+
declare const DialogTitle: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogTitleProps & React.RefAttributes<HTMLHeadingElement>, "ref"> & React.RefAttributes<HTMLHeadingElement>>;
|
|
18
|
+
declare const DialogDescription: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogDescriptionProps & React.RefAttributes<HTMLParagraphElement>, "ref"> & React.RefAttributes<HTMLParagraphElement>>;
|
|
19
|
+
export { Dialog, DialogPortal, DialogOverlay, DialogClose, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, };
|
|
20
|
+
//# sourceMappingURL=dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/ui/components/dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAC;AAK1D,QAAA,MAAM,MAAM,uCAAuB,CAAC;AAEpC,QAAA,MAAM,aAAa,8GAA0B,CAAC;AAE9C,QAAA,MAAM,YAAY,6CAAyB,CAAC;AAE5C,QAAA,MAAM,WAAW,4GAAwB,CAAC;AAE1C,QAAA,MAAM,aAAa,8JAYjB,CAAC;AAGH,QAAA,MAAM,aAAa,8JAqBjB,CAAC;AAGH,QAAA,MAAM,YAAY;8BAGf,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;;CAQtC,CAAC;AAGF,QAAA,MAAM,YAAY;8BAGf,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;;CAQtC,CAAC;AAGF,QAAA,MAAM,WAAW,oKAYf,CAAC;AAGH,QAAA,MAAM,iBAAiB,8KASrB,CAAC;AAGH,OAAO,EACL,MAAM,EACN,YAAY,EACZ,aAAa,EACb,WAAW,EACX,aAAa,EACb,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
5
|
+
import { X } from 'lucide-react';
|
|
6
|
+
import { cn } from 'fumadocs-ui/components/api';
|
|
7
|
+
const Dialog = DialogPrimitive.Root;
|
|
8
|
+
const DialogTrigger = DialogPrimitive.Trigger;
|
|
9
|
+
const DialogPortal = DialogPrimitive.Portal;
|
|
10
|
+
const DialogClose = DialogPrimitive.Close;
|
|
11
|
+
const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, className: cn('fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-fd-fade-in data-[state=closed]:animate-fd-fade-out', className), ...props })));
|
|
12
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
13
|
+
const DialogContent = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn('fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-fd-popover p-6 shadow-lg duration-200 data-[state=open]:animate-fd-dialog-in data-[state=closed]:animate-fd-dialog-out sm:rounded-lg', className), ...props, children: [children, _jsxs(DialogPrimitive.Close, { className: "absolute end-4 top-4 rounded-sm opacity-70 ring-offset-fd-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-fd-accent data-[state=open]:text-fd-muted-foreground", children: [_jsx(X, { className: "size-4" }), _jsx("span", { className: "sr-only", children: "Close" })] })] })] })));
|
|
14
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
15
|
+
const DialogHeader = ({ className, ...props }) => (_jsx("div", { className: cn('flex flex-col space-y-1.5 text-center sm:text-start', className), ...props }));
|
|
16
|
+
DialogHeader.displayName = 'DialogHeader';
|
|
17
|
+
const DialogFooter = ({ className, ...props }) => (_jsx("div", { className: cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-2', className), ...props }));
|
|
18
|
+
DialogFooter.displayName = 'DialogFooter';
|
|
19
|
+
const DialogTitle = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Title, { ref: ref, className: cn('text-lg font-semibold leading-none tracking-tight', className), ...props })));
|
|
20
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
21
|
+
const DialogDescription = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Description, { ref: ref, className: cn('text-sm text-fd-muted-foreground', className), ...props })));
|
|
22
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
23
|
+
export { Dialog, DialogPortal, DialogOverlay, DialogClose, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, };
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { cn } from 'fumadocs-ui/components/api';
|
|
4
4
|
import { cva } from 'class-variance-authority';
|
|
5
|
-
export const labelVariants = cva('text-[13px] font-
|
|
5
|
+
export const labelVariants = cva('text-[13px] font-medium text-fd-card-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70');
|
|
6
6
|
const Input = React.forwardRef(({ className, type, ...props }, ref) => {
|
|
7
7
|
return (_jsx("input", { type: type, className: cn('flex h-9 w-full rounded-md border bg-transparent px-2 py-1.5 text-[13px] text-fd-foreground transition-colors placeholder:text-fd-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-fd-ring disabled:cursor-not-allowed disabled:opacity-50', className), ref: ref, ...props }));
|
|
8
8
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export declare function useQuery<I, T>(fn: (input: I) => Promise<T>): {
|
|
2
|
-
start: (input: I) => void;
|
|
1
|
+
export declare function useQuery<I extends unknown[], T>(fn: (...input: I) => Promise<T>): {
|
|
2
|
+
start: (...input: I) => void;
|
|
3
3
|
data?: T;
|
|
4
|
+
error?: unknown;
|
|
4
5
|
isLoading: boolean;
|
|
5
6
|
};
|
|
6
7
|
//# sourceMappingURL=use-query.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-query.d.ts","sourceRoot":"","sources":["../../src/utils/use-query.ts"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"use-query.d.ts","sourceRoot":"","sources":["../../src/utils/use-query.ts"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,CAAC,EAC7C,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAC9B;IACD,KAAK,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC7B,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB,CAgCA"}
|
package/dist/utils/use-query.js
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
|
-
import { useMemo, useState } from 'react';
|
|
1
|
+
import { useMemo, useRef, useState } from 'react';
|
|
2
2
|
export function useQuery(fn) {
|
|
3
3
|
const [loading, setLoading] = useState(false);
|
|
4
4
|
const [data, setData] = useState();
|
|
5
|
+
const [error, setError] = useState();
|
|
6
|
+
const fnRef = useRef(fn);
|
|
7
|
+
fnRef.current = fn;
|
|
5
8
|
return useMemo(() => ({
|
|
6
9
|
isLoading: loading,
|
|
7
10
|
data,
|
|
8
|
-
|
|
11
|
+
error,
|
|
12
|
+
start(...input) {
|
|
9
13
|
setLoading(true);
|
|
10
|
-
void
|
|
14
|
+
void fnRef
|
|
15
|
+
.current(...input)
|
|
11
16
|
.then((res) => {
|
|
12
17
|
setData(res);
|
|
18
|
+
setError(undefined);
|
|
13
19
|
})
|
|
14
|
-
.catch(() => {
|
|
20
|
+
.catch((err) => {
|
|
15
21
|
setData(undefined);
|
|
22
|
+
setError(err);
|
|
16
23
|
})
|
|
17
24
|
.finally(() => {
|
|
18
25
|
setLoading(false);
|
|
19
26
|
});
|
|
20
27
|
},
|
|
21
|
-
}), [
|
|
28
|
+
}), [error, data, loading]);
|
|
22
29
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fumadocs-openapi",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.0",
|
|
4
4
|
"description": "Generate MDX docs for your OpenAPI spec",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NextJs",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@fumari/json-schema-to-typescript": "^1.1.2",
|
|
47
|
+
"@radix-ui/react-dialog": "^1.1.6",
|
|
47
48
|
"@radix-ui/react-select": "^2.1.6",
|
|
48
49
|
"@radix-ui/react-slot": "^1.1.2",
|
|
49
50
|
"@scalar/openapi-parser": "0.10.8",
|
|
@@ -61,8 +62,8 @@
|
|
|
61
62
|
"remark-rehype": "^11.1.1",
|
|
62
63
|
"shiki": "^3.0.0",
|
|
63
64
|
"xml-js": "^1.6.11",
|
|
64
|
-
"fumadocs-core": "15.0.
|
|
65
|
-
"fumadocs-ui": "15.0.
|
|
65
|
+
"fumadocs-core": "15.0.13",
|
|
66
|
+
"fumadocs-ui": "15.0.13"
|
|
66
67
|
},
|
|
67
68
|
"devDependencies": {
|
|
68
69
|
"@scalar/api-client-react": "^1.1.31",
|