chore: website tooling (#41)

This commit is contained in:
Darius Cepulis
2025-10-08 15:35:50 -05:00
committed by GitHub
parent d2a0b27272
commit 1bb8166704
53 changed files with 5218 additions and 326 deletions
@@ -0,0 +1,15 @@
---
import { type SupportedFramework } from '@/types/docs';
interface Props {
frameworks?: SupportedFramework[];
}
const { frameworks } = Astro.props;
const { framework } = Astro.params;
// Only render if current framework matches, or if no frameworks specified (all frameworks)
const shouldRender = !frameworks || frameworks.includes(framework as SupportedFramework);
---
{shouldRender && <slot />}
+143
View File
@@ -0,0 +1,143 @@
import type { AnySupportedStyle, SupportedFramework } from '@/types/docs';
import { navigate } from 'astro:transitions/client';
import { getAvailableStyles, getDefaultStyle, SUPPORTED_FRAMEWORKS } from '@/types/docs';
import { findFirstGuide, findGuideBySlug, getValidStylesForGuide } from '@/utils/docs/sidebar';
interface SelectorProps {
currentFramework: SupportedFramework;
currentStyle: AnySupportedStyle;
}
/**
* Extract the current guide slug from the docs URL.
* URL format: /docs/framework/{framework}/style/{style}/{slug}/
* @returns The guide slug (everything after the style parameter)
*/
function getCurrentGuideSlug(): string {
const pathParts = window.location.pathname.split('/').filter(Boolean);
const styleIndex = pathParts.indexOf('style');
const slugParts = pathParts.slice(styleIndex + 2); // Everything after the style value
return slugParts.join('/');
}
export function Selectors({ currentFramework, currentStyle }: SelectorProps) {
const handleFrameworkChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const newFramework = event.target.value as SupportedFramework;
// Get current guide slug from URL
const currentGuideSlug = getCurrentGuideSlug();
// Find the current guide in the sidebar
const currentGuide = findGuideBySlug(currentGuideSlug);
if (!currentGuide) {
// No current guide found, redirect to first guide of new framework
const firstGuide = findFirstGuide(newFramework, getDefaultStyle(newFramework));
if (firstGuide) {
navigate(`/docs/framework/${newFramework}/style/${getDefaultStyle(newFramework)}/${firstGuide}/`);
} else {
navigate('/docs/');
}
return;
}
// Check if guide is available for the new framework
if (currentGuide.frameworks && !currentGuide.frameworks.includes(newFramework)) {
// Guide not available in new framework, redirect to first guide
const firstGuide = findFirstGuide(newFramework, getDefaultStyle(newFramework));
if (firstGuide) {
navigate(`/docs/framework/${newFramework}/style/${getDefaultStyle(newFramework)}/${firstGuide}/`);
} else {
navigate('/docs/');
}
return;
}
// Get valid styles for this guide in the new framework
const validStyles = getValidStylesForGuide(currentGuide, newFramework);
if (validStyles.length === 0) {
// Guide not available in new framework, go to first guide
const firstGuide = findFirstGuide(newFramework, getDefaultStyle(newFramework));
if (firstGuide) {
navigate(`/docs/framework/${newFramework}/style/${getDefaultStyle(newFramework)}/${firstGuide}/`);
} else {
navigate('/docs/');
}
return;
}
// Pick best style: current if still valid, otherwise first valid
const newStyle = validStyles.some((s) => s === currentStyle) ? currentStyle : validStyles[0];
// Navigate to same guide with adjusted framework/style
navigate(`/docs/framework/${newFramework}/style/${newStyle}/${currentGuideSlug}/`);
};
const handleStyleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const newStyle = event.target.value as AnySupportedStyle;
// Get current guide slug from URL
const currentGuideSlug = getCurrentGuideSlug();
// Find the current guide in the sidebar
const currentGuide = findGuideBySlug(currentGuideSlug);
if (!currentGuide) {
// No current guide found, redirect to first guide of new style
const firstGuide = findFirstGuide(currentFramework, newStyle);
if (firstGuide) {
navigate(`/docs/framework/${currentFramework}/style/${newStyle}/${firstGuide}/`);
} else {
navigate('/docs/');
}
return;
}
// Check if guide is valid for current framework and new style
const validStyles = getValidStylesForGuide(currentGuide, currentFramework);
if (!validStyles.some((s) => s === newStyle)) {
// Guide not available for new style, go to first guide
const firstGuide = findFirstGuide(currentFramework, newStyle);
if (firstGuide) {
navigate(`/docs/framework/${currentFramework}/style/${newStyle}/${firstGuide}/`);
} else {
navigate('/docs/');
}
return;
}
// Guide supports the new style, navigate to it
navigate(`/docs/framework/${currentFramework}/style/${newStyle}/${currentGuideSlug}/`);
};
const availableStyles = getAvailableStyles(currentFramework);
return (
<div className="mb-4">
<div>
<label htmlFor="framework-select">Framework:</label>
<select id="framework-select" value={currentFramework} onChange={handleFrameworkChange}>
{SUPPORTED_FRAMEWORKS.map((fw) => (
<option key={fw} value={fw}>
{fw}
</option>
))}
</select>
</div>
<div>
<label htmlFor="style-select">Style:</label>
<select id="style-select" value={currentStyle} onChange={handleStyleChange}>
{availableStyles.map((st) => (
<option key={st} value={st}>
{st}
</option>
))}
</select>
</div>
</div>
);
}
@@ -0,0 +1,40 @@
---
import {
type SupportedFramework,
type AnySupportedStyle,
type Guide,
type Section,
isSection,
} from '@/types/docs';
type Props = {
item: Guide | Section;
framework: SupportedFramework;
style: AnySupportedStyle;
docTitles: Map<string, string>;
};
const { item, framework, style, docTitles } = Astro.props;
---
{
isSection(item) ? (
<details open>
<summary>{item.sidebarLabel}</summary>
{item.contents.map((contentItem) => (
<Astro.self
item={contentItem}
framework={framework}
style={style}
docTitles={docTitles}
/>
))}
</details>
) : (
<div class="sidebar-guide">
<a href={`/docs/framework/${framework}/style/${style}/${item.slug}/`}>
{item.sidebarLabel || docTitles.get(item.slug) || item.slug}
</a>
</div>
)
}
@@ -0,0 +1,15 @@
---
import type { AnySupportedStyle } from '@/types/docs';
interface Props {
styles?: AnySupportedStyle[];
}
const { styles } = Astro.props;
const { style } = Astro.params;
// Only render if current style matches, or if no styles specified (all styles)
const shouldRender = !styles || styles.includes(style as AnySupportedStyle);
---
{shouldRender && <slot />}