Co-authored-by: Nick Quidas <nrfq@users.noreply.github.com>
parent
c3577ccbf7
commit
84b7a9d100
File diff suppressed because it is too large
Load Diff
|
@ -8,14 +8,15 @@
|
||||||
"@testing-library/react": "^11.2.7",
|
"@testing-library/react": "^11.2.7",
|
||||||
"@testing-library/user-event": "^12.8.3",
|
"@testing-library/user-event": "^12.8.3",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.24.0",
|
||||||
"express": "^4.17.1",
|
|
||||||
"fuse": "^0.4.0",
|
|
||||||
"fuse.js": "^6.4.6",
|
"fuse.js": "^6.4.6",
|
||||||
"jQuery": "^1.7.4",
|
"jQuery": "^1.7.4",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-cookie": "^4.1.1",
|
"react-cookie": "^4.1.1",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
"react-markdown": "^8.0.2",
|
||||||
|
"react-router-dom": "^6.3.0",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
|
"rehype-slug": "^5.0.1",
|
||||||
"web-vitals": "^1.1.2"
|
"web-vitals": "^1.1.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -31,7 +31,7 @@ h2 {
|
||||||
|
|
||||||
h3, h4, h5, h6 {
|
h3, h4, h5, h6 {
|
||||||
color:#494949;
|
color:#494949;
|
||||||
font-weight: 500;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@ -339,7 +339,7 @@ a{
|
||||||
}
|
}
|
||||||
|
|
||||||
.result {
|
.result {
|
||||||
width: 30%;
|
/* width: 30%; */
|
||||||
padding: 0.25em 0.5em;
|
padding: 0.25em 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
309
src/App.js
309
src/App.js
|
@ -1,4 +1,5 @@
|
||||||
import React, { useState, useEffect, createContext } from "react";
|
import React, { useState, useEffect, createContext } from "react";
|
||||||
|
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
||||||
import LangFilters from "./components/LangFilters";
|
import LangFilters from "./components/LangFilters";
|
||||||
import SectDropdown from "./components/SectDropdown";
|
import SectDropdown from "./components/SectDropdown";
|
||||||
import SearchBar from "./components/SearchBar";
|
import SearchBar from "./components/SearchBar";
|
||||||
|
@ -6,72 +7,17 @@ import SearchResult from "./components/SearchResult";
|
||||||
import LightSwitch from "./components/LightSwitch";
|
import LightSwitch from "./components/LightSwitch";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import { ThemeContext, themes, swapMode } from './darkMode';
|
import { ThemeContext, themes, swapMode } from "./darkMode";
|
||||||
import { useCookies } from 'react-cookie';
|
import { useCookies } from "react-cookie";
|
||||||
|
|
||||||
import Default from "./components/Default";
|
import Default from "./components/Default";
|
||||||
|
|
||||||
import SunImg from "./img/sun.png";
|
import SunImg from "./img/sun.png";
|
||||||
import MoonImg from "./img/moon.png";
|
import MoonImg from "./img/moon.png";
|
||||||
|
import BookList from "./components/BookList";
|
||||||
|
|
||||||
const fpb = null;
|
const fpb = null;
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
function makeBook(author, hLang, cLang, title, url) {
|
|
||||||
//returns a struct with basic book info (author, human language, computer language, book title, url)
|
|
||||||
return {
|
|
||||||
author: author,
|
|
||||||
hLang: hLang, //human language
|
|
||||||
cLang: cLang, //computer language
|
|
||||||
title: title,
|
|
||||||
url: url,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
function forEachBook(func, json) {
|
|
||||||
//Runs func on each section, entry, and book in json, which is a list of entries
|
|
||||||
if (typeof func !== "function") {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
throw "ERROR in forEachBook: parameter not a fucntion";
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const hLang in json) {
|
|
||||||
//for each human language
|
|
||||||
if (Array.isArray(hLang.sections)) {
|
|
||||||
//check if sections is an array
|
|
||||||
hLang.sections.forEach(
|
|
||||||
(
|
|
||||||
cLang //for each computer lanuage
|
|
||||||
) => {
|
|
||||||
if (Array.isArray(cLang.entries)) {
|
|
||||||
//verify is entries is an array
|
|
||||||
cLang.entries.forEach(
|
|
||||||
(
|
|
||||||
book //for each book
|
|
||||||
) => {
|
|
||||||
if (typeof book === "object") {
|
|
||||||
//verify that book is an object
|
|
||||||
func(json[hLang], cLang, book); //run the function
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sorts search results by their score
|
|
||||||
// eslint-disable-next-line
|
|
||||||
function sortByScore(results) {
|
|
||||||
results.sort(function (a, b) {
|
|
||||||
return a.score - b.score;
|
|
||||||
});
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jsonToArray(json) {
|
function jsonToArray(json) {
|
||||||
let arr = [];
|
let arr = [];
|
||||||
let sections = [];
|
let sections = [];
|
||||||
|
@ -112,8 +58,7 @@ function App() {
|
||||||
const [loading, setLoading] = useState(true); // Determines whether to show spinner
|
const [loading, setLoading] = useState(true); // Determines whether to show spinner
|
||||||
const [searchParams, setSearchParams] = useState({ searchTerm: "" });
|
const [searchParams, setSearchParams] = useState({ searchTerm: "" });
|
||||||
const [searchResults, setSearchResults] = useState([]);
|
const [searchResults, setSearchResults] = useState([]);
|
||||||
const [sectionResults, setSectionResults] = useState([]);
|
const [cookies, setCookie, removeCookie] = useCookies(["lightMode"]);
|
||||||
const [cookies, setCookie, removeCookie] = useCookies(['lightMode']);
|
|
||||||
|
|
||||||
const [lightMode, setLightMode] = useState(true);
|
const [lightMode, setLightMode] = useState(true);
|
||||||
|
|
||||||
|
@ -121,7 +66,6 @@ const [cookies, setCookie, removeCookie] = useCookies(['lightMode']);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
let resultsList = null; // the html string containing the search results
|
let resultsList = null; // the html string containing the search results
|
||||||
let sectionResultsList = null;
|
|
||||||
|
|
||||||
const changeParameter = (param, value) => {
|
const changeParameter = (param, value) => {
|
||||||
// Lets a child component set the value of the search term
|
// Lets a child component set the value of the search term
|
||||||
|
@ -130,7 +74,7 @@ const [cookies, setCookie, removeCookie] = useCookies(['lightMode']);
|
||||||
|
|
||||||
// fetches data the first time the page renders
|
// fetches data the first time the page renders
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
swapMode(cookies.lightMode ? themes.lightMode : themes.darkMode)
|
swapMode(cookies.lightMode ? themes.lightMode : themes.darkMode);
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
@ -154,7 +98,7 @@ const [cookies, setCookie, removeCookie] = useCookies(['lightMode']);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// fires when searchTerm changes
|
// fires when searchTerm changes
|
||||||
// Finds most relevant title or author
|
// Finds most relevant title or author
|
||||||
// THIS IS THE MAIN SEARCH FUNCTION
|
// THIS IS THE MAIN SEARCH FUNCTION
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dataArray) {
|
if (dataArray) {
|
||||||
|
@ -177,17 +121,17 @@ const [cookies, setCookie, removeCookie] = useCookies(['lightMode']);
|
||||||
if (value === null || value === "") continue;
|
if (value === null || value === "") continue;
|
||||||
if (key === "lang.code" || key === "section") {
|
if (key === "lang.code" || key === "section") {
|
||||||
// the '^' means it must be an exact match at the beginning
|
// the '^' means it must be an exact match at the beginning
|
||||||
// this is because lang.code and section are strict filters
|
// this is because lang.code and section are strict filters
|
||||||
andQuery.push({ [key]: `^${value}` });
|
andQuery.push({ [key]: `^${value}` });
|
||||||
}
|
}
|
||||||
if(key === "searchTerm") {
|
if (key === "searchTerm") {
|
||||||
orQuery.push({ "author": value});
|
orQuery.push({ author: value });
|
||||||
orQuery.push({ "title": value});
|
orQuery.push({ title: value });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Nest the 'or' query inside the 'and' query
|
// Nest the 'or' query inside the 'and' query
|
||||||
// Necessary step, a quirk with fuse.js
|
// Necessary step, a quirk with fuse.js
|
||||||
andQuery.push({$or: orQuery})
|
andQuery.push({ $or: orQuery });
|
||||||
// Perform the search
|
// Perform the search
|
||||||
let result = fuse.search({
|
let result = fuse.search({
|
||||||
$and: andQuery,
|
$and: andQuery,
|
||||||
|
@ -215,7 +159,7 @@ const [cookies, setCookie, removeCookie] = useCookies(['lightMode']);
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = section;
|
let id = section;
|
||||||
|
|
||||||
// Some ids are in HTML tags, so this will extract that id to form proper links
|
// Some ids are in HTML tags, so this will extract that id to form proper links
|
||||||
if (id.includes("<a")) {
|
if (id.includes("<a")) {
|
||||||
let x = id.match(/"(.*?)"/)[0];
|
let x = id.match(/"(.*?)"/)[0];
|
||||||
|
@ -251,15 +195,6 @@ const [cookies, setCookie, removeCookie] = useCookies(['lightMode']);
|
||||||
result = relevantLists.concat(result);
|
result = relevantLists.concat(result);
|
||||||
setSearchResults(result);
|
setSearchResults(result);
|
||||||
// console.log(result);
|
// console.log(result);
|
||||||
|
|
||||||
// No longer needed as the sections aren't being used
|
|
||||||
// let sResults = []; // section results
|
|
||||||
// // Finds the most relevant sections
|
|
||||||
// result.forEach((entry) => {
|
|
||||||
// let section = entry.item.section;
|
|
||||||
// if (!sResults.includes(section)) sResults.push(section);
|
|
||||||
// });
|
|
||||||
// setSectionResults(sResults);
|
|
||||||
}
|
}
|
||||||
}, [searchParams, dataArray]);
|
}, [searchParams, dataArray]);
|
||||||
|
|
||||||
|
@ -276,128 +211,108 @@ const [cookies, setCookie, removeCookie] = useCookies(['lightMode']);
|
||||||
searchResults.map((entry) => {
|
searchResults.map((entry) => {
|
||||||
return <SearchResult data={entry.item} />;
|
return <SearchResult data={entry.item} />;
|
||||||
});
|
});
|
||||||
// Getting rid of the section results UI renders this irrelevant
|
|
||||||
// sectionResultsList =
|
|
||||||
// sectionResults &&
|
|
||||||
// sectionResults.map((section) => {
|
|
||||||
// return (
|
|
||||||
// <button
|
|
||||||
// onClick={() => {
|
|
||||||
// changeParameter("section", section);
|
|
||||||
// }}
|
|
||||||
// >
|
|
||||||
// {section}
|
|
||||||
// </button>
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
<BrowserRouter>
|
||||||
<div
|
<div className="wrapper">
|
||||||
className="wrapper"
|
<ThemeContext.Consumer>
|
||||||
// style={{
|
{({ changeTheme }) => {
|
||||||
// color: lightMode ? "black" : "white",
|
let willBeDarkMode = cookies.lightMode && cookies.lightMode.toLowerCase() !== "true"; //whether or not we are currently light mode and will become dark mode
|
||||||
// backgroundColor: lightMode ? "white" : "black",
|
changeTheme(willBeDarkMode ? themes.light : themes.dark);
|
||||||
// }}
|
return (
|
||||||
>
|
<img
|
||||||
<ThemeContext.Consumer>
|
src={willBeDarkMode ? MoonImg : SunImg}
|
||||||
{ ({ changeTheme }) => {
|
onClick={() => {
|
||||||
let willBeDarkMode = (cookies.lightMode && cookies.lightMode.toLowerCase() !== "true") //whether or not we are currently light mode and will become dark mode
|
setCookie("lightMode", willBeDarkMode);
|
||||||
changeTheme(willBeDarkMode ? themes.light : themes.dark)
|
changeTheme(willBeDarkMode ? themes.light : themes.dark);
|
||||||
return (<img src={willBeDarkMode ? MoonImg : SunImg}
|
}}
|
||||||
onClick = {()=>{
|
style={{ width: "20px", height: "20px", display: "block", marginLeft: "auto" }}
|
||||||
setCookie("lightMode",willBeDarkMode);
|
/>
|
||||||
changeTheme(willBeDarkMode ? themes.light : themes.dark)
|
);
|
||||||
}}
|
}}
|
||||||
style={{width: "20px", height: "20px",display: "block",
|
</ThemeContext.Consumer>
|
||||||
marginLeft: "auto"
|
<header className="header">
|
||||||
}}
|
<h1>
|
||||||
/>)}
|
<a href="/free-programming-books-search/">
|
||||||
}
|
free-programming-books
|
||||||
</ThemeContext.Consumer>
|
|
||||||
<header className="header">
|
|
||||||
|
|
||||||
<h1>
|
|
||||||
<a href="https://ebookfoundation.github.io/free-programming-books/" target="_blank" rel="noreferrer">
|
|
||||||
free-programming-books
|
|
||||||
</a>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<img
|
|
||||||
className="emoji"
|
|
||||||
title=":books:"
|
|
||||||
alt=":books:"
|
|
||||||
src="https://github.githubassets.com/images/icons/emoji/unicode/1f4da.png"
|
|
||||||
height="20"
|
|
||||||
width="20"
|
|
||||||
/>{" "}
|
|
||||||
Freely available programming books
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p className="view">
|
|
||||||
<a href="https://github.com/EbookFoundation/free-programming-books" target="_blank" rel="noreferrer">
|
|
||||||
View the Project on GitHub <small>EbookFoundation/free-programming-books</small>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Does a link not work?
|
|
||||||
<br />
|
|
||||||
<a href="https://github.com/EbookFoundation/free-programming-books/issues/" target="_blank" rel="noreferrer">
|
|
||||||
Report an error on GitHub
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<SearchBar changeParameter={changeParameter} />
|
|
||||||
{/* Filters */}
|
|
||||||
<LangFilters changeParameter={changeParameter} data={data} />
|
|
||||||
{/* Keeping sections commented out just in case */}
|
|
||||||
{/* <SectDropdown
|
|
||||||
className="sect-drop"
|
|
||||||
changeParameter={changeParameter}
|
|
||||||
data={data}
|
|
||||||
value={searchParams["section"] || "allSects"}
|
|
||||||
/> */}
|
|
||||||
{/* {sectionResultsList && <h3>Suggestions based on your search</h3>}
|
|
||||||
<div className="search-results section-results">{sectionResultsList}</div> */}
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<section className="search-results">
|
|
||||||
{resultsList ? (
|
|
||||||
<div>
|
|
||||||
<br />
|
|
||||||
<h2>Search Results</h2>
|
|
||||||
<ul>{resultsList}</ul>
|
|
||||||
</div>
|
|
||||||
) : searchParams.searchTerm ? (
|
|
||||||
<div>
|
|
||||||
<br />
|
|
||||||
<h2>No results found.</h2>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Default />
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<p>
|
|
||||||
This project is maintained by{" "}
|
|
||||||
<a href="https://github.com/EbookFoundation" target="_blank" rel="noreferrer">
|
|
||||||
EbookFoundation
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<small>
|
|
||||||
Hosted on GitHub Pages — Theme by{" "}
|
|
||||||
<a href="https://github.com/orderedlist" target="_blank" rel="noreferrer">
|
|
||||||
orderedlist
|
|
||||||
</a>
|
</a>
|
||||||
</small>
|
</h1>
|
||||||
</p>
|
|
||||||
</footer>
|
<p>
|
||||||
</div>
|
<img
|
||||||
|
className="emoji"
|
||||||
|
title=":books:"
|
||||||
|
alt=":books:"
|
||||||
|
src="https://github.githubassets.com/images/icons/emoji/unicode/1f4da.png"
|
||||||
|
height="20"
|
||||||
|
width="20"
|
||||||
|
/>{" "}
|
||||||
|
Freely available programming books
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="view">
|
||||||
|
<a href="https://github.com/EbookFoundation/free-programming-books" target="_blank" rel="noreferrer">
|
||||||
|
View the Project on GitHub <small>EbookFoundation/free-programming-books</small>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Does a link not work?
|
||||||
|
<br />
|
||||||
|
<a
|
||||||
|
href="https://github.com/EbookFoundation/free-programming-books/issues/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Report an error on GitHub
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<SearchBar changeParameter={changeParameter} />
|
||||||
|
<LangFilters changeParameter={changeParameter} data={data} langCode={searchParams["lang.code"]} />
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section className="search-results">
|
||||||
|
{resultsList ? (
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
<h2>Search Results</h2>
|
||||||
|
<ul>{resultsList}</ul>
|
||||||
|
</div>
|
||||||
|
) : searchParams.searchTerm ? (
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
<h2>No results found.</h2>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Routes>
|
||||||
|
<Route path="/free-programming-books-search" element={<Default />} />
|
||||||
|
<Route
|
||||||
|
path="/free-programming-books-search/books/:lang"
|
||||||
|
element={<BookList changeParameter={changeParameter} />}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
<footer>
|
||||||
|
<p>
|
||||||
|
This project is maintained by{" "}
|
||||||
|
<a href="https://github.com/EbookFoundation" target="_blank" rel="noreferrer">
|
||||||
|
EbookFoundation
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<small>
|
||||||
|
Hosted on GitHub Pages — Theme by{" "}
|
||||||
|
<a href="https://github.com/orderedlist" target="_blank" rel="noreferrer">
|
||||||
|
orderedlist
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import axios from "axios";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import rehypeSlug from 'rehype-slug'
|
||||||
|
|
||||||
|
function BookList({changeParameter}) {
|
||||||
|
let [markdown, setMarkdown] = useState(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
let params = useParams();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
changeParameter('lang.code', params.lang);
|
||||||
|
}, [params.lang]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchData() {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
let result = await axios.get(
|
||||||
|
`https://raw.githubusercontent.com/EbookFoundation/free-programming-books/main/books/free-programming-books-${params.lang}.md`
|
||||||
|
);
|
||||||
|
setMarkdown(result.data);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Couldn't get data. Please try again later")
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
fetchData();
|
||||||
|
}, []);
|
||||||
|
if(loading){
|
||||||
|
return <p>Loading...</p>
|
||||||
|
}
|
||||||
|
console.log(markdown);
|
||||||
|
return <section><ReactMarkdown children={markdown} rehypePlugins={[rehypeSlug]} /></section>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BookList;
|
|
@ -69,6 +69,135 @@ function Default() {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<h2 id="resources">Resources</h2>
|
||||||
|
|
||||||
|
<p>This project lists books and other resources grouped by genres:</p>
|
||||||
|
|
||||||
|
<h3 id="books">Books</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="/free-programming-books-search/books/langs/">English, By Programming Language</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="/free-programming-books-search/books/subjects/">English, By Subject</a>
|
||||||
|
</p>
|
||||||
|
<h4 id="other-languages">Other Languages</h4>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/ar/">Arabic / al arabiya / العربية</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/az/">Azerbaijani / Азәрбајҹан дили / آذربايجانجا ديلي</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/bn/">Bengali / বাংলা</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/bg/">Bulgarian / български</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/my/">Burmese / မြန်မာဘာသာ</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/zh/">Chinese / 中文</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/cs/">Czech / čeština / český jazyk</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/dk/">Danish / dansk</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/nl/">Dutch / Nederlands</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/et/">Estonian / eesti keel</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/fi/">Finnish / suomi / suomen kieli</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/fr/">French / français</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/de/">German / Deutsch</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/el/">Greek / ελληνικά</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/he/">Hebrew / עברית</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/hi/">Hindi / हिन्दी</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/hu/">Hungarian / magyar / magyar nyelv</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/id/">Indonesian / Bahasa Indonesia</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/it/">Italian / italiano</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/ja/">Japanese / 日本語</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/ko/">Korean / 한국어 [韓國語]</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/no/">Norwegian / Norsk</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/fa_IR/">Persian / Farsi (Iran) / فارسى</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/pl/">Polish / polski / język polski / polszczyzna</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/pt_BR/">Portuguese (Brazil)</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/pt_PT/">Portuguese (Portugal)</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/ro/">Romanian (Romania) / limba română / român</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/ru/">Russian / Русский язык</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/sr/">Serbian / српски језик / srpski jezik</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/sk/">Slovak / slovenčina</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/es/">Spanish / español / castellano</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/sv/">Swedish / Svenska</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/ta/">Tamil / தமிழ்</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/th/">Thai / ไทย</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/tr/">Turkish / Türkçe</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/uk/">Ukrainian / Українська</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/free-programming-books-search/books/vi/">Vietnamese / Tiếng Việt</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2 id="translations">Translations</h2>
|
<h2 id="translations">Translations</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
function LangFilters({ changeParameter, data }) {
|
function LangFilters({ changeParameter, data, langCode }) {
|
||||||
const [languages, setLanguages] = useState([]);
|
const [languages, setLanguages] = useState([]);
|
||||||
const [selected, setSelected] = useState("");
|
const [selected, setSelected] = useState("");
|
||||||
const [showFilters, setShow] = useState(false);
|
const [showFilters, setShow] = useState(false);
|
||||||
|
@ -11,6 +11,10 @@ function LangFilters({ changeParameter, data }) {
|
||||||
setSelected(e.target.value);
|
setSelected(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelected(langCode);
|
||||||
|
}, [langCode]);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
// run whenever data changes
|
// run whenever data changes
|
||||||
() => {
|
() => {
|
||||||
|
@ -60,7 +64,14 @@ function LangFilters({ changeParameter, data }) {
|
||||||
let filterList = (
|
let filterList = (
|
||||||
<form className="filters">
|
<form className="filters">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" key="all" className="sect-select" value="" onChange={handleChange} checked={"" == selected} />
|
<input
|
||||||
|
type="radio"
|
||||||
|
key="all"
|
||||||
|
className="sect-select"
|
||||||
|
value=""
|
||||||
|
onChange={handleChange}
|
||||||
|
checked={"" == selected}
|
||||||
|
/>
|
||||||
All Languages
|
All Languages
|
||||||
</label>
|
</label>
|
||||||
{options}
|
{options}
|
||||||
|
@ -71,7 +82,7 @@ function LangFilters({ changeParameter, data }) {
|
||||||
<div className="langFilters">
|
<div className="langFilters">
|
||||||
<div className="filterHeader">
|
<div className="filterHeader">
|
||||||
<h3>Filter by Language</h3>
|
<h3>Filter by Language</h3>
|
||||||
<button onClick={() => setShow(!showFilters)}>{showFilters? "-" : "+"}</button>
|
<button onClick={() => setShow(!showFilters)}>{showFilters ? "-" : "+"}</button>
|
||||||
</div>
|
</div>
|
||||||
{showFilters ? filterList : ""}
|
{showFilters ? filterList : ""}
|
||||||
</div>
|
</div>
|
||||||
|
|
21092
src/fpb.json
21092
src/fpb.json
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue