import { useEffect, useState } from 'react' import Cookies from 'js-cookie' import { SubNav, TabNav, UnderlineNav } from '@primer/components' import { sendEvent, EventType } from 'components/lib/events' import { useRouter } from 'next/router' import { useArticleContext } from 'components/context/ArticleContext' import parseUserAgent from 'components/lib/user-agent' const platforms = [ { id: 'mac', label: 'Mac' }, { id: 'windows', label: 'Windows' }, { id: 'linux', label: 'Linux' }, ] // Nota bene: platform === os // Imperatively modify article content to show only the selected platform // find all platform-specific *block* elements and hide or show as appropriate // example: {% mac %} block content {% endmac %} function showPlatformSpecificContent(platform: string) { const markdowns = Array.from(document.querySelectorAll('.extended-markdown')) markdowns .filter((el) => platforms.some((platform) => el.classList.contains(platform.id))) .forEach((el) => { el.style.display = el.classList.contains(platform) ? '' : 'none' }) // find all platform-specific *inline* elements and hide or show as appropriate // example: inline content const platformEls = Array.from( document.querySelectorAll( platforms.map((platform) => `.platform-${platform.id}`).join(', ') ) ) platformEls.forEach((el) => { el.style.display = el.classList.contains(`platform-${platform}`) ? '' : 'none' }) } // uses the order of the supportedPlatforms array to // determine the default platform const getFallbackPlatform = (detectedPlatforms: Array): string => { const foundPlatform = platforms.find((platform) => detectedPlatforms.includes(platform.id)) return foundPlatform?.id || 'linux' } type Props = { variant?: 'subnav' | 'tabnav' | 'underlinenav' } export const PlatformPicker = ({ variant = 'subnav' }: Props) => { const { defaultPlatform, detectedPlatforms } = useArticleContext() const [currentPlatform, setCurrentPlatform] = useState(defaultPlatform || '') const { asPath } = useRouter() // Run on mount for client-side only features useEffect(() => { let userAgent = parseUserAgent().os if (userAgent === 'ios') { userAgent = 'mac' } const platform = defaultPlatform || Cookies.get('osPreferred') || userAgent || 'linux' setCurrentPlatform(platform) // always trigger this on initial render. if the default doesn't change the other useEffect won't fire showPlatformSpecificContent(platform) }, [asPath]) // Make sure we've always selected a platform that exists in the article useEffect(() => { // Only check *after* current platform has been determined if (currentPlatform && !detectedPlatforms.includes(currentPlatform)) { setCurrentPlatform(getFallbackPlatform(detectedPlatforms)) } }, [currentPlatform, detectedPlatforms.join(',')]) const onClickPlatform = (platform: string) => { setCurrentPlatform(platform) // imperatively modify the article content showPlatformSpecificContent(platform) sendEvent({ type: EventType.preference, preference_name: 'os', preference_value: platform, }) Cookies.set('osPreferred', platform, { sameSite: 'strict', secure: true, }) } // only show platforms that are in the current article const platformOptions = platforms.filter((platform) => detectedPlatforms.includes(platform.id)) const sharedContainerProps = { 'data-testid': 'platform-picker', 'aria-label': 'Platform picker', 'data-default-platform': defaultPlatform, className: 'mb-4', } if (variant === 'subnav') { return ( {platformOptions.map((option) => { return ( { onClickPlatform(option.id) }} > {option.label} ) })} ) } if (variant === 'underlinenav') { return ( {platformOptions.map((option) => { return ( { onClickPlatform(option.id) }} > {option.label} ) })} ) } return ( {platformOptions.map((option) => { return ( { onClickPlatform(option.id) }} > {option.label} ) })} ) }