import { StickyElement, StickyViewport } from '@allejo/react-position-sticky'; import { saveAs } from 'file-saver'; import hljs from 'highlight.js'; import { GetServerSideProps } from 'next'; import { FormEvent, SyntheticEvent, createContext, useCallback, useContext, useEffect, useRef, useState, } from 'react'; import { Alert, AlertTypes } from '../components/alert'; import { BlurredBackground } from '../components/blurredbackground'; import { Checkbox } from '../components/Checkbox'; import SearchableText from '../components/searchable-text'; import LANG_CATS from '../data/categories.json'; import { MainLayout } from '../layouts/main'; import { classList } from '../utilities/cssClasses'; export const getServerSideProps: GetServerSideProps = async ({ req }) => { // For backward compatability, redirect any POST requests made the `/download` // URL that was used by the Django version of this website; the new URL is a // Next.js API route. if (req.method === 'POST') { return { redirect: { permanent: false, destination: '/api/download', }, }; } return { props: {}, }; }; enum SelectionState { None, Some, All, } interface PageContext { addLanguage: (lang: string | string[]) => void; delLanguage: (lang: string | string[]) => void; filter: string; selectedLanguages: string[]; setFilter: (filter: string) => void; } const PageContext = createContext({ addLanguage: () => {}, delLanguage: () => {}, filter: '', selectedLanguages: [], setFilter: () => {}, }); const LanguageList = () => { const { delLanguage, selectedLanguages } = useContext(PageContext); return (

Selected Languages:

{selectedLanguages.length === 0 ? ( None Selected ) : ( )}
); }; const BundlerControls = () => { const { filter, setFilter, selectedLanguages } = useContext(PageContext); const containerRef = useRef(); const [height, setHeight] = useState(0); const handleFilterChange = (e: SyntheticEvent) => { setFilter(e.currentTarget.value); }; useEffect(() => { if (containerRef.current === null) { return; } setHeight(containerRef.current.offsetHeight); }, [selectedLanguages]); return ( <>
); }; interface LanguageCheckboxProps { language: string; onFilterResults: (language: string, matched: boolean) => void; } const LanguageCheckbox = ({ language, onFilterResults, }: LanguageCheckboxProps) => { const { addLanguage, delLanguage, filter, selectedLanguages } = useContext(PageContext); const isFilterActive = !!filter; const [matchFound, setMatchFound] = useState(false); const handleLanguageSelection = (event: SyntheticEvent) => { if (event.currentTarget.checked) { addLanguage(language); } else { delLanguage(language); } }; useEffect(() => onFilterResults(language, matchFound), [matchFound]); return ( ); }; interface LanguageCategoryProps { category: string; } const LanguageCategory = ({ category }: LanguageCategoryProps) => { const [selection, setSelection] = useState(SelectionState.None); const [hasMatches, setHasMatches] = useState(false); const [langMatches, setLangMatches] = useState>({}); const { filter, addLanguage, delLanguage, selectedLanguages } = useContext(PageContext); const isFilterActive = !!filter; const handleSelectAll = useCallback( (event: SyntheticEvent) => { if (event.currentTarget.checked || selection === SelectionState.Some) { addLanguage(LANG_CATS[category]); } else { delLanguage(LANG_CATS[category]); } }, [], ); const handleFilterResults = (lang: string, matched: boolean) => { setLangMatches((prevState) => ({ ...prevState, [lang]: matched, })); }; useEffect(() => { if (!isFilterActive) { return; } setHasMatches(Object.values(langMatches).some((v) => v === true)); }, [langMatches]); useEffect(() => { const langs = new Set(selectedLanguages); let langCount = 0; for (const lang of LANG_CATS[category]) { if (langs.has(lang)) { langCount++; } } if (langCount === 0) { setSelection(SelectionState.None); } else if (langCount === LANG_CATS[category].length) { setSelection(SelectionState.All); } else { setSelection(SelectionState.Some); } }, [selectedLanguages]); return (
{category}
{LANG_CATS[category].map((language) => ( ))}
); }; const Download = () => { const [filter, setFilter] = useState(''); const [selectedLanguages, setSelectedLanguages] = useState([]); const editLanguage = (languages: string | string[], add: boolean) => { return (currLangs: string[]) => { const newLangs = new Set(currLangs); const fxnCall = add ? 'add' : 'delete'; if (Array.isArray(languages)) { languages.forEach((lang) => newLangs[fxnCall](lang)); } else { newLangs[fxnCall](languages); } return Array.from(newLangs).sort(); }; }; const addLanguage = (language: string | string[]) => { setSelectedLanguages(editLanguage(language, true)); }; const delLanguage = (language: string | string[]) => { setSelectedLanguages(editLanguage(language, false)); }; const handleOnSelectAll = (event: SyntheticEvent) => { if (event.currentTarget.checked) { setSelectedLanguages(hljs.listLanguages()); } else { setSelectedLanguages([]); } }; const handleOnSubmit = (event: FormEvent) => { event.preventDefault(); const formData = new FormData(event.currentTarget); const languages = formData.getAll('languages'); fetch('/api/download', { method: 'POST', body: JSON.stringify({ api: 2, languages, }), headers: { 'Content-Type': 'application/json', }, }) .then((response) => response.blob()) .then((response) => { const blob = new Blob([response], { type: 'application/json' }); saveAs(blob, 'highlight.zip'); }); }; const allLangSelected = selectedLanguages.length === hljs.listLanguages().length; return (
The design of the download bundle has changed and may be slightly different than what you're expecting. See{' '} PR #10 {' '} for more information.
{Object.keys(LANG_CATS).map((category) => ( ))}
); }; export default Download;