Files
v10/website/src/components/docs/Selectors.tsx
T

144 lines
5.1 KiB
TypeScript

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.includes(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.includes(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>
);
}