fix(api): Remove duplicated Authentication section
parent
7b34cf1855
commit
0ab9a15a5c
|
|
@ -293,6 +293,127 @@ function createRapiDocElement(
|
|||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject custom styles into RapiDoc's shadow DOM
|
||||
* Removes the top border and reduces whitespace above operations
|
||||
*/
|
||||
function injectShadowStyles(element: HTMLElement): void {
|
||||
const tryInject = (): boolean => {
|
||||
const shadowRoot = (element as unknown as { shadowRoot: ShadowRoot | null })
|
||||
.shadowRoot;
|
||||
if (!shadowRoot) return false;
|
||||
|
||||
// Check if styles already injected
|
||||
if (shadowRoot.querySelector('#rapidoc-custom-styles')) return true;
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.id = 'rapidoc-custom-styles';
|
||||
style.textContent = `
|
||||
/* Hide the operation divider line */
|
||||
.divider[part="operation-divider"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Reduce spacing above operation sections */
|
||||
.section-gap {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
/* Hide RapiDoc's built-in security section - we show our own */
|
||||
/* Target the authorization requirements shown near each operation */
|
||||
.api-key,
|
||||
.api-key-info,
|
||||
.security-info-button,
|
||||
[class*="api-key"],
|
||||
[class*="security-info"],
|
||||
.m-markdown-small:has(.lock-icon),
|
||||
div:has(> .lock-icon),
|
||||
/* Target the section showing "AUTHORIZATIONS:" or similar */
|
||||
.req-resp-container > div:first-child:has(svg[style*="lock"]),
|
||||
/* Target lock icons and their parent containers */
|
||||
svg.lock-icon,
|
||||
.lock-icon,
|
||||
/* Wide selectors for security-related elements */
|
||||
[part="section-operation-security"],
|
||||
.expanded-endpoint-body > div:first-child:has([class*="lock"]) {
|
||||
display: none !important;
|
||||
}
|
||||
`;
|
||||
shadowRoot.appendChild(style);
|
||||
|
||||
// Hide security badge elements by examining content
|
||||
const hideSecurityBadge = () => {
|
||||
// Find elements containing security-related text and hide their container
|
||||
const allElements = shadowRoot.querySelectorAll('span, div');
|
||||
allElements.forEach((el) => {
|
||||
const text = el.textContent?.trim();
|
||||
// Find leaf elements that contain authorization-related text
|
||||
if (
|
||||
el.children.length === 0 &&
|
||||
(text === 'HTTP Bearer' ||
|
||||
text === 'Bearer' ||
|
||||
text === 'AUTHORIZATIONS:' ||
|
||||
text === 'Authorization' ||
|
||||
text === 'api_token' ||
|
||||
text === 'BearerAuthentication')
|
||||
) {
|
||||
// Walk up the DOM to find a suitable container to hide
|
||||
// This hides both the text AND any sibling icons (like lock)
|
||||
let target: HTMLElement = el as HTMLElement;
|
||||
let parent: HTMLElement | null = el.parentElement;
|
||||
let depth = 0;
|
||||
while (parent && depth < 4) {
|
||||
// Stop at reasonable container boundaries
|
||||
if (
|
||||
parent.classList.contains('expanded-endpoint-body') ||
|
||||
parent.classList.contains('req-resp-container') ||
|
||||
parent.tagName === 'SECTION'
|
||||
) {
|
||||
break;
|
||||
}
|
||||
target = parent;
|
||||
parent = parent.parentElement;
|
||||
depth++;
|
||||
}
|
||||
target.style.display = 'none';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Run immediately and after delays for dynamic content
|
||||
hideSecurityBadge();
|
||||
setTimeout(hideSecurityBadge, 300);
|
||||
setTimeout(hideSecurityBadge, 800);
|
||||
|
||||
// Watch for dynamically added security elements
|
||||
const observer = new MutationObserver(() => {
|
||||
hideSecurityBadge();
|
||||
});
|
||||
observer.observe(shadowRoot, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
|
||||
// Disconnect after 5 seconds to avoid performance issues
|
||||
setTimeout(() => observer.disconnect(), 5000);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Try immediately
|
||||
if (tryInject()) return;
|
||||
|
||||
// Retry a few times as shadow DOM may not be ready
|
||||
let attempts = 0;
|
||||
const maxAttempts = 10;
|
||||
const interval = setInterval(() => {
|
||||
attempts++;
|
||||
if (tryInject() || attempts >= maxAttempts) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch for theme changes and update RapiDoc element
|
||||
*/
|
||||
|
|
@ -398,6 +519,9 @@ export default async function RapiDocMini({
|
|||
const rapiDocElement = createRapiDocElement(specUrl, matchPaths, title);
|
||||
component.appendChild(rapiDocElement);
|
||||
|
||||
// Inject custom styles into shadow DOM to remove borders/spacing
|
||||
injectShadowStyles(rapiDocElement);
|
||||
|
||||
// Watch for theme changes and return cleanup function
|
||||
return watchThemeChanges(component);
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@
|
|||
margin: 0;
|
||||
|
||||
code {
|
||||
background: $g3-castle;
|
||||
background: $article-code-bg;
|
||||
color: $article-code;
|
||||
padding: 0.2em 0.5em;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9em;
|
||||
|
|
@ -78,10 +79,26 @@
|
|||
// Positioned above RapiDoc, integrates with "Try it" via JavaScript API.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
.api-auth-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.api-auth-trigger-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.api-auth-schemes {
|
||||
font-size: 0.85rem;
|
||||
color: $g9-mountain;
|
||||
|
||||
.api-auth-label {
|
||||
font-weight: 400;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.api-auth-trigger {
|
||||
|
|
@ -340,6 +357,10 @@
|
|||
// Dark theme overrides
|
||||
[data-theme="dark"],
|
||||
html:has(link[title="dark-theme"]:not([disabled])) {
|
||||
.api-auth-schemes {
|
||||
color: $g15-platinum;
|
||||
}
|
||||
|
||||
.api-security-schemes {
|
||||
border-top-color: $grey25;
|
||||
|
||||
|
|
@ -352,10 +373,6 @@ html:has(link[title="dark-theme"]:not([disabled])) {
|
|||
dt {
|
||||
color: $g15-platinum;
|
||||
}
|
||||
|
||||
dd code {
|
||||
background: $grey20;
|
||||
}
|
||||
}
|
||||
|
||||
.scheme-description {
|
||||
|
|
|
|||
|
|
@ -87,8 +87,10 @@
|
|||
{{ end }}
|
||||
</section>
|
||||
|
||||
{{/* Security Schemes from OpenAPI spec */}}
|
||||
{{/* Security Schemes from OpenAPI spec (only show if showSecuritySchemes: true) */}}
|
||||
{{ if .Params.showSecuritySchemes }}
|
||||
{{ partial "api/security-schemes.html" . }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{/* Operation Page - RapiDoc with custom slots */}}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,25 +41,41 @@
|
|||
<link rel="alternate" type="application/json" href="{{ $specPathJSON | absURL }}" title="OpenAPI Specification (JSON)" />
|
||||
{{ end }}
|
||||
|
||||
{{/* Auth credentials popover trigger - positioned above the operation */}}
|
||||
<div class="api-auth-trigger-wrapper">
|
||||
<button type="button"
|
||||
class="api-auth-trigger btn btn-sm"
|
||||
data-component="api-auth-input"
|
||||
data-schemes="{{ $authSchemes }}"
|
||||
data-popover="true"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="dialog">
|
||||
<svg class="auth-icon" width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
||||
<path d="M8 1a3.5 3.5 0 0 0-3.5 3.5V6H3a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1h-1.5V4.5A3.5 3.5 0 0 0 8 1zm2 5H6V4.5a2 2 0 1 1 4 0V6z"/>
|
||||
</svg>
|
||||
<span class="auth-trigger-text">Set credentials</span>
|
||||
<span class="auth-status-indicator" hidden aria-label="Credentials configured"></span>
|
||||
</button>
|
||||
{{/* Popover container - positioned by CSS */}}
|
||||
<div class="api-auth-popover" role="dialog" aria-label="API credentials" hidden>
|
||||
{{/* Content rendered by TypeScript component */}}
|
||||
{{/* Auth credentials popover trigger with scheme indicator */}}
|
||||
{{/* Map scheme codes to display names */}}
|
||||
{{ $schemeNames := dict "bearer" "HTTP Bearer" "token" "API Token" "basic" "HTTP Basic" "querystring" "Query String" }}
|
||||
{{ $schemeList := split $authSchemes "," }}
|
||||
{{ $displaySchemes := slice }}
|
||||
{{ range $schemeList }}
|
||||
{{ $name := index $schemeNames . }}
|
||||
{{ if $name }}
|
||||
{{ $displaySchemes = $displaySchemes | append $name }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
<div class="api-auth-row">
|
||||
<div class="api-auth-trigger-wrapper">
|
||||
<button type="button"
|
||||
class="api-auth-trigger btn btn-sm"
|
||||
data-component="api-auth-input"
|
||||
data-schemes="{{ $authSchemes }}"
|
||||
data-popover="true"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="dialog">
|
||||
<svg class="auth-icon" width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
||||
<path d="M8 1a3.5 3.5 0 0 0-3.5 3.5V6H3a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1h-1.5V4.5A3.5 3.5 0 0 0 8 1zm2 5H6V4.5a2 2 0 1 1 4 0V6z"/>
|
||||
</svg>
|
||||
<span class="auth-trigger-text">Set credentials</span>
|
||||
<span class="auth-status-indicator" hidden aria-label="Credentials configured"></span>
|
||||
</button>
|
||||
{{/* Popover container - positioned by CSS */}}
|
||||
<div class="api-auth-popover" role="dialog" aria-label="API credentials" hidden>
|
||||
{{/* Content rendered by TypeScript component */}}
|
||||
</div>
|
||||
</div>
|
||||
<span class="api-auth-schemes" title="Supported authentication methods">
|
||||
<span class="api-auth-label">Auth:</span> {{ delimit $displaySchemes " or " }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{{/* Component container - TypeScript handles initialization */}}
|
||||
|
|
@ -95,7 +111,7 @@
|
|||
|
||||
.api-reference-mini {
|
||||
width: 100%;
|
||||
margin: 1rem 0;
|
||||
margin: 0;
|
||||
position: relative; /* Contains absolutely positioned auth elements */
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue