📝 docs: Add documentation
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Scalar API Reference</title>
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #0b0e13;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
html::-webkit-scrollbar,
|
||||
body::-webkit-scrollbar,
|
||||
#scalar-root::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#scalar-root {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.scalar-app .scalar-api-reference.references-layout,
|
||||
.scalar-app .references-layout {
|
||||
min-height: auto !important;
|
||||
}
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="scalar-root"></div>
|
||||
<script src="./scalar-embed.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,193 @@
|
||||
(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const spec = params.get("spec") || "/swagger.json";
|
||||
const title = params.get("title") || "API Reference";
|
||||
const theme = params.get("theme") || "saturn";
|
||||
const layout = params.get("layout") || "classic";
|
||||
const operationId = params.get("operationId");
|
||||
const scalarRoot = document.getElementById("scalar-root");
|
||||
const getHeightSourceElement = () =>
|
||||
document.querySelector(".narrow-references-container") || scalarRoot;
|
||||
let resizeObserver;
|
||||
const observedElements = new WeakSet();
|
||||
|
||||
const sendHeightToParent = () => {
|
||||
if (window.parent === window) return;
|
||||
const heightSource = getHeightSourceElement();
|
||||
const sourceHeight =
|
||||
heightSource?.scrollHeight ?? heightSource?.offsetHeight ?? 0;
|
||||
const fallbackHeight = Math.max(
|
||||
document.body.scrollHeight,
|
||||
document.documentElement.scrollHeight,
|
||||
0
|
||||
);
|
||||
const height = heightSource ? sourceHeight : fallbackHeight;
|
||||
window.parent.postMessage(
|
||||
{
|
||||
type: "scalar-height",
|
||||
height,
|
||||
},
|
||||
window.location.origin
|
||||
);
|
||||
};
|
||||
|
||||
const mountScalar = (sourceConfiguration) => {
|
||||
Scalar.createApiReference("#scalar-root", {
|
||||
title,
|
||||
theme,
|
||||
layout,
|
||||
showSidebar: false,
|
||||
hideModels: true,
|
||||
defaultOpenAllTags: true,
|
||||
documentDownloadType: "json",
|
||||
hideSearch: false,
|
||||
withDefaultFonts: true,
|
||||
showOperationId: Boolean(operationId),
|
||||
...sourceConfiguration,
|
||||
});
|
||||
|
||||
requestAnimationFrame(sendHeightToParent);
|
||||
};
|
||||
|
||||
const filterSpecByOperationId = (specContent, requestedId) => {
|
||||
if (
|
||||
!specContent ||
|
||||
typeof specContent !== "object" ||
|
||||
!specContent.paths ||
|
||||
typeof specContent.paths !== "object"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filteredPaths = {};
|
||||
const matchedTags = new Set();
|
||||
let hasMatch = false;
|
||||
|
||||
Object.entries(specContent.paths).forEach(([path, methods]) => {
|
||||
if (!methods || typeof methods !== "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
const retainedOperations = Object.entries(methods).reduce(
|
||||
(acc, [method, operation]) => {
|
||||
if (
|
||||
operation &&
|
||||
typeof operation === "object" &&
|
||||
operation.operationId === requestedId
|
||||
) {
|
||||
acc[method] = operation;
|
||||
hasMatch = true;
|
||||
if (Array.isArray(operation.tags)) {
|
||||
operation.tags.forEach((tag) => matchedTags.add(tag));
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
if (Object.keys(retainedOperations).length > 0) {
|
||||
filteredPaths[path] = retainedOperations;
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasMatch) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filteredSpec = {
|
||||
...specContent,
|
||||
paths: filteredPaths,
|
||||
};
|
||||
|
||||
if (Array.isArray(specContent.tags) && matchedTags.size > 0) {
|
||||
filteredSpec.tags = specContent.tags.filter((tag) =>
|
||||
matchedTags.has(tag.name)
|
||||
);
|
||||
}
|
||||
|
||||
delete filteredSpec["x-scalar-navigation"];
|
||||
|
||||
return filteredSpec;
|
||||
};
|
||||
|
||||
const fetchSpecContent = async () => {
|
||||
const response = await fetch(spec);
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to load spec (${response.status} ${response.statusText})`
|
||||
);
|
||||
}
|
||||
|
||||
const text = await response.text();
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
"Scalar embed: Spec is not valid JSON, falling back to full document.",
|
||||
error
|
||||
);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
if (operationId) {
|
||||
try {
|
||||
const specContent = await fetchSpecContent();
|
||||
const filteredSpec = filterSpecByOperationId(specContent, operationId);
|
||||
if (filteredSpec) {
|
||||
mountScalar({ content: filteredSpec });
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
"Scalar embed: Unable to filter by the requested operationId.",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mountScalar({ url: spec });
|
||||
};
|
||||
|
||||
init();
|
||||
|
||||
const ensureResizeObservers = () => {
|
||||
if (typeof ResizeObserver === "undefined" || !scalarRoot) return;
|
||||
if (!resizeObserver) {
|
||||
resizeObserver = new ResizeObserver(() => {
|
||||
requestAnimationFrame(sendHeightToParent);
|
||||
});
|
||||
}
|
||||
[scalarRoot, getHeightSourceElement()]
|
||||
.filter((element) => element && !observedElements.has(element))
|
||||
.forEach((element) => {
|
||||
resizeObserver.observe(element);
|
||||
observedElements.add(element);
|
||||
});
|
||||
};
|
||||
|
||||
const mutationObserver =
|
||||
typeof MutationObserver !== "undefined" && scalarRoot
|
||||
? new MutationObserver(() => {
|
||||
ensureResizeObservers();
|
||||
requestAnimationFrame(sendHeightToParent);
|
||||
})
|
||||
: null;
|
||||
|
||||
mutationObserver?.observe(scalarRoot, { childList: true, subtree: true });
|
||||
ensureResizeObservers();
|
||||
|
||||
if (typeof ResizeObserver === "undefined" || !scalarRoot) {
|
||||
const intervalId = setInterval(() => {
|
||||
if (document.readyState === "complete") {
|
||||
sendHeightToParent();
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
window.addEventListener("load", sendHeightToParent);
|
||||
window.addEventListener("resize", sendHeightToParent);
|
||||
})();
|
||||
Reference in New Issue
Block a user