refactor(api): Replace inline JS with data-component in rapidoc.html

Remove ~230 lines of inline JavaScript and use the new api-rapidoc
TypeScript component via data-component attribute instead.
worktree-2025-12-30T19-16-55
Jason Stirnaman 2025-12-12 13:12:38 -06:00
parent 15bf7ec3ce
commit b5cf8967f5
1 changed files with 1 additions and 230 deletions

View File

@ -30,7 +30,7 @@
<link rel="alternate" type="application/json" href="{{ $specPathJSON | absURL }}" title="OpenAPI Specification (JSON)" />
{{ end }}
<div class="api-reference-wrapper">
<div class="api-reference-wrapper" data-component="api-rapidoc">
{{/* RapiDoc component with slot-based customization */}}
<rapi-doc
id="api-doc"
@ -94,235 +94,6 @@
{{/* Load RapiDoc from CDN */}}
<script type="module" src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"></script>
<script>
(function() {
'use strict';
console.log('[RapiDoc] Script loaded');
// Detect current theme by checking which theme stylesheet is enabled
function isDarkTheme() {
// Check for enabled dark-theme stylesheet
const darkStylesheet = document.querySelector('link[rel*="stylesheet"][title="dark-theme"]:not([disabled])');
if (darkStylesheet && !darkStylesheet.disabled) {
return true;
}
// Fallback: check for data-theme attribute (some pages may use this)
if (document.documentElement.dataset.theme === 'dark') {
return true;
}
// Fallback: check localStorage preference
try {
const stored = localStorage.getItem('defined_style_preference');
if (stored) {
const prefs = JSON.parse(stored);
return prefs.theme === 'dark';
}
} catch (e) {
// Ignore localStorage errors
}
return false;
}
// Update RapiDoc theme based on document theme
function updateRapiDocTheme() {
const rapiDoc = document.getElementById('api-doc');
if (!rapiDoc) return;
const isDark = isDarkTheme();
if (isDark) {
// Match Hugo dark theme: $grey10: #14141F
rapiDoc.setAttribute('theme', 'dark');
rapiDoc.setAttribute('bg-color', '#14141F');
rapiDoc.setAttribute('text-color', '#D4D7DD');
rapiDoc.setAttribute('header-color', '#D4D7DD');
rapiDoc.setAttribute('primary-color', '#a0a0ff');
rapiDoc.setAttribute('nav-bg-color', '#1a1a2a');
rapiDoc.setAttribute('nav-text-color', '#D4D7DD');
rapiDoc.setAttribute('nav-hover-bg-color', '#252535');
rapiDoc.setAttribute('nav-hover-text-color', '#ffffff');
rapiDoc.setAttribute('nav-accent-color', '#a0a0ff');
rapiDoc.setAttribute('code-theme', 'monokai');
} else {
// Match Hugo light theme: $g20-white: #FFFFFF
rapiDoc.setAttribute('theme', 'light');
rapiDoc.setAttribute('bg-color', '#ffffff');
rapiDoc.setAttribute('text-color', '#2b2b2b');
rapiDoc.setAttribute('header-color', '#020a47');
rapiDoc.setAttribute('primary-color', '#020a47');
rapiDoc.setAttribute('nav-bg-color', '#f7f8fa');
rapiDoc.setAttribute('nav-text-color', '#2b2b2b');
rapiDoc.setAttribute('nav-hover-bg-color', '#e8e8f0');
rapiDoc.setAttribute('nav-hover-text-color', '#020a47');
rapiDoc.setAttribute('nav-accent-color', '#020a47');
rapiDoc.setAttribute('code-theme', 'prism');
}
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', updateRapiDocTheme);
} else {
updateRapiDocTheme();
}
// Set custom CSS properties for input border styling
function setInputBorderStyles() {
const rapiDoc = document.getElementById('api-doc');
if (!rapiDoc) {
console.log('[RapiDoc] Element not found');
return;
}
// Set CSS custom property on the rapi-doc element
// This will penetrate the shadow DOM
rapiDoc.style.setProperty('--border-color', '#00A3FF');
console.log('[RapiDoc] Set --border-color to #00A3FF');
}
// Wait for RapiDoc custom element to be fully defined
if (customElements && customElements.whenDefined) {
customElements.whenDefined('rapi-doc').then(() => {
console.log('[RapiDoc] Custom element defined');
setInputBorderStyles();
// Set again after render
setTimeout(setInputBorderStyles, 500);
});
} else {
// Fallback for older browsers
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', setInputBorderStyles);
} else {
setInputBorderStyles();
}
setTimeout(setInputBorderStyles, 500);
}
// Watch for stylesheet changes (theme toggles enable/disable stylesheets)
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
// Check if a stylesheet's disabled attribute changed
if (mutation.type === 'attributes' &&
mutation.target.tagName === 'LINK' &&
mutation.target.title &&
mutation.target.title.includes('theme')) {
updateRapiDocTheme();
}
// Also watch for data-theme changes as a fallback
if (mutation.attributeName === 'data-theme') {
updateRapiDocTheme();
}
});
});
// Observe the head element for stylesheet changes
observer.observe(document.head, {
attributes: true,
attributeFilter: ['disabled'],
subtree: true
});
// Also observe the document element for data-theme changes
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme']
});
// Hide "Expand all | Collapse all" controls and Overview section inside shadow DOM
function hideExpandCollapseControls() {
const rapiDoc = document.getElementById('api-doc');
if (!rapiDoc) return;
// Wait for RapiDoc to render, try multiple times
let attempts = 0;
const maxAttempts = 10;
const tryHide = function() {
attempts++;
try {
const shadowRoot = rapiDoc.shadowRoot;
if (!shadowRoot) {
if (attempts < maxAttempts) {
setTimeout(tryHide, 500);
}
return;
}
// Find all elements in shadow DOM and hide those containing "Expand all"
const allElements = shadowRoot.querySelectorAll('*');
let hiddenCount = 0;
allElements.forEach(function(element) {
const text = element.textContent || '';
// Check if element contains "Expand all", "Collapse all"
if (text.includes('Expand all') || text.includes('Collapse all')) {
// Hide the element and its parent containers
element.style.display = 'none';
if (element.parentElement) {
element.parentElement.style.display = 'none';
}
hiddenCount++;
}
});
// Separately target all headings and hide those with "Overview"
const headings = shadowRoot.querySelectorAll('h1, h2, h3, h4');
headings.forEach(function(heading) {
const text = (heading.textContent || '').trim();
const innerText = (heading.innerText || '').trim();
// Log for debugging
if (text.includes('Overview') || innerText.includes('Overview')) {
console.log('[RapiDoc] Found heading:', heading.tagName, 'with text:', text, 'innerText:', innerText);
heading.style.display = 'none';
hiddenCount++;
}
});
// Also inject CSS as backup to hide Overview sections
const style = document.createElement('style');
style.textContent = `
/* Hide Overview section and headings */
.section-gap.section-tag,
[id*="overview"],
.regular-font.section-gap:empty,
h1:empty, h2:empty, h3:empty {
display: none !important;
}
`;
shadowRoot.appendChild(style);
console.log('[RapiDoc] Hidden ' + hiddenCount + ' elements containing expand/collapse text (attempt ' + attempts + ')');
if (hiddenCount === 0 && attempts < maxAttempts) {
// Try again if nothing was hidden
setTimeout(tryHide, 500);
}
} catch (e) {
console.log('[RapiDoc] Could not access shadow DOM:', e);
if (attempts < maxAttempts) {
setTimeout(tryHide, 500);
}
}
};
setTimeout(tryHide, 500);
}
// Call after RapiDoc initializes
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', hideExpandCollapseControls);
} else {
hideExpandCollapseControls();
}
})();
</script>
<style>
.api-reference-wrapper {
width: 100%;