UNPKG

fumadocs-openapi

Version:

Generate MDX docs for your OpenAPI spec

101 lines (100 loc) 4.87 kB
'use client'; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { createContext, useContext, useEffect, useMemo, useRef, useState, } from 'react'; import { useApiContext, useServerSelectContext } from '../../ui/contexts/api.js'; import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../ui/components/select.js'; import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event'; import { joinURL, resolveRequestData, resolveServerUrl, withBase, } from '../../utils/url.js'; const CodeExampleContext = createContext(null); export function CodeExampleProvider({ route, examples, initialKey, children, }) { const [key, setKey] = useState(initialKey ?? examples[0].key); const listeners = useRef([]); const setData = useEffectEvent((data, encoded) => { for (const example of examples) { if (example.key === key) { // persistent changes example.data = data; example.encoded = encoded; break; } } for (const listener of listeners.current) { listener(data, encoded); } }); const updateKey = useEffectEvent((newKey) => { const example = examples.find((example) => example.key === newKey); if (!example) return; setKey(newKey); for (const listener of listeners.current) { listener(example.data, example.encoded); } }); const addListener = useEffectEvent((listener) => { // initial call to listeners to ensure their data is the latest // this is necessary to avoid race conditions between `useEffect()` const example = examples.find((example) => example.key === key); listener(example.data, example.encoded); listeners.current.push(listener); }); const removeListener = useEffectEvent((listener) => { listeners.current = listeners.current.filter((item) => item !== listener); }); return (_jsx(CodeExampleContext, { value: useMemo(() => ({ key, route, setKey: updateKey, examples, setData, removeListener, addListener, }), [addListener, examples, key, removeListener, route, setData, updateKey]), children: children })); } export function CodeExample(sample) { const { shikiOptions, mediaAdapters } = useApiContext(); const { examples, key, route, addListener, removeListener } = useContext(CodeExampleContext); const { server } = useServerSelectContext(); const [data, setData] = useState(() => { return examples.find((example) => example.key === key).encoded; }); useEffect(() => { const listener = (_, encoded) => setData(encoded); addListener(listener); return () => { removeListener(listener); }; }, [addListener, removeListener]); const code = useMemo(() => { if (!sample.source) return; if (typeof sample.source === 'string') return sample.source; return sample.source(joinURL(withBase(server ? resolveServerUrl(server.url, server.variables) : '/', typeof window !== 'undefined' ? window.location.origin : 'https://loading'), resolveRequestData(route, data)), data, { server: sample.serverContext, mediaAdapters, }); }, [mediaAdapters, sample, server, route, data]); if (!code || !sample) return null; return (_jsx(DynamicCodeBlock, { lang: sample.lang, code: code, options: shikiOptions })); } export function CodeExampleSelector({ items }) { const { key, setKey } = useContext(CodeExampleContext); const item = items.find((item) => item.value === key); return (_jsxs(Select, { value: key, onValueChange: setKey, children: [_jsx(SelectTrigger, { className: "not-prose mb-2", children: _jsx(SelectValue, { asChild: true, children: item ? _jsx(SelectDisplay, { item: item }) : null }) }), _jsx(SelectContent, { children: items.map((item) => (_jsx(SelectItem, { value: item.value, children: _jsx(SelectDisplay, { item: item }) }, item.value))) })] })); } function SelectDisplay({ item, ...props }) { return (_jsxs("div", { ...props, children: [_jsx("span", { className: "font-medium text-sm", children: item.title }), _jsx("span", { className: "text-fd-muted-foreground", children: item.description })] })); } export function useRequestInitialData() { const { examples, key } = useContext(CodeExampleContext); return useMemo(() => examples.find((example) => example.key === key).data, [examples, key]); } export function useRequestDataUpdater() { const { setData } = useContext(CodeExampleContext); return { setData }; }