filter by language

pull/6/head
nrfq 2021-12-02 16:55:12 -05:00
parent c3cbc18814
commit 0524b18250
5 changed files with 159 additions and 99 deletions

View File

@ -1,43 +1,32 @@
.App {
text-align: center;
body{
box-sizing: border-box;
background-color: #222222;
}
.App-logo {
height: 40vmin;
pointer-events: none;
#root {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
#frontPage {
color: blue;
text-align: center;
}
.App-header {
background-color: #282c34;
min-height: 100vh;
.frontPage {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
height: 100;
color: whitesmoke;
text-align: center;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
.search-results{
display: flex;
flex-direction: row;
flex-wrap: wrap;
box-sizing: border-box;
justify-content: space-around;
padding-left: 10%;
padding-right: 10%;
}

View File

@ -1,9 +1,12 @@
import React, { useState, useEffect, Component } from 'react';
import LangDropdown from './components/LangDropdown';
import SearchBar from './components/SearchBar';
import SearchResult from './components/SearchResult';
import axios from 'axios';
import Fuse from 'fuse.js';
const fpb = require('./fpb.json');
function makeBook(author, hLang, cLang, title, url)
{
//returns a struct with basic book info (author, human language, computer language, book title, url)
@ -24,9 +27,9 @@ function forEachBook(func, json) //Runs func on each section, entry, and book in
for (const hLang in json) //for each human language
{
if (Array.isArray(json[hLang].sections)) //check if sections is an array
if (Array.isArray(hLang.sections)) //check if sections is an array
{
json[hLang].sections.forEach(
hLang.sections.forEach(
(cLang) => //for each computer lanuage
{if (Array.isArray(cLang.entries)) //verify is entries is an array
{
@ -97,24 +100,68 @@ function sortByScore(results){
return results;
}
function App() {
const [ data, setData ] = useState(undefined);
const [ loading, setLoading ] = useState(true); //Determines whether to show spinner
const [ searchTerm, setSearchTerm ] = useState('');
const [ searchResults, setSearchResults ] = useState([]);
let resultsList = null; // the html string containing the search results
const setSearch = (term) => { // Lets a child set the value of the search term
setSearchTerm(term);
function jsonToArray(json){
let arr = [];
let sections = [];
json.children[0].children.forEach(
(document) => {
document.sections.forEach(
(section) => {
if(!sections.includes(section.section))
sections.push(section.section);
section.entries.forEach(
(entry) => {
arr.push({author: entry.author, title: entry.title, url: entry.url, lang: document.language, section: section.section});
}
)
section.subsections.forEach(
(subsection) => {
subsection.entries.forEach(
(entry) => {
arr.push({author: entry.author, title: entry.title, url: entry.url, lang: document.language, section: section.section, subsection: subsection.section});
}
)
}
)
}
)
}
)
return {arr: arr, sections: sections};
}
function App() {
const [ data, setData ] = useState(undefined); // keeps the state of the json
const [ dataArray, setDataArray ] = useState([]); // put everything into one array. uses more memory, but search is faster and less complex
const [ index, setIndex ] = useState([]);
const [ loading, setLoading ] = useState(true); //Determines whether to show spinner
const [ searchParams, setSearchParams ] = useState({title: ''});
const [ searchResults, setSearchResults ] = useState([]);
const [ error, setError ] = useState('');
let resultsList = null; // the html string containing the search results
let sectionResults = null;
const changeParameter = (param, value) => { // Lets a child set the value of the search term
setSearchParams({...searchParams, [param]: value});
};
// fetches data the first time the page renders
useEffect(
() => {
async function fetchData() {
try{
setLoading(true);
let result = await axios.get('https://raw.githubusercontent.com/FreeEbookFoundationBot/free-programming-books-json/main/fpb.json');
setData(result.data);
let { arr, sections } = jsonToArray(result.data);
setDataArray(arr);
setIndex(sections);
}
catch(e){
setError("Couldn't get data. Please try again later")
}
setLoading(false);
}
fetchData();
@ -126,62 +173,67 @@ function App() {
// THIS IS THE MAIN SEARCH FUNCTION CURRENTLY
useEffect(
() => {
if(data){
let result = [];
data.children[0].children.forEach( (document) => {
document.sections.forEach( (section) => {
if(dataArray){
const fuseOptions = {
useExtendedSearch: true,
findAllMatches: true,
shouldSort: false,
shouldSort: true,
includeScore: true,
threshold: 0.3,
keys: ['title']
};
let fuse = new Fuse(section.entries, fuseOptions);
let fuseResult = fuse.search(searchTerm);
result = result.concat(fuseResult);
section.subsections.forEach( (subsection) => {
let fuse = new Fuse(subsection.entries, fuseOptions);
let fuseResult = fuse.search(searchTerm);
result = result.concat(fuseResult);
threshold: 0.2,
keys: ['title', 'lang.code']
}
let fuse = new Fuse(dataArray, fuseOptions);
let query = [];
for (const [key, value] of Object.entries(searchParams)) {
if(value == null || value == '') continue;
if(key == 'lang.code'){
query.push({'lang.code': `^${value}`});
continue
}
query.push({[key]: value});
}
let result = fuse.search({
$and: query
});
});
});
result = sortByScore(result);
setSearchResults(result);
setSearchResults(result.slice(0, 40));
}
},
[ searchTerm ]
[ searchParams ]
)
const buildList = () => {
};
if(loading){ // if still fetching resource
return(
<h1>Loading...</h1>
);
}
if(searchTerm && searchResults.length !== 0){
if(error){
return(
<h1>Error: {error}</h1>
)
}
if(searchParams.title && searchResults.length !== 0){
resultsList =
searchResults &&
searchResults.map((entry) => {
return (<li><a href={entry.item.url}>{entry.item.title}</a></li>)
return <SearchResult data={entry.item}/>
// return (<li><a href={entry.item.url}>{entry.item.title}</a></li>)
});
}
console.log(data);
return(
<div>
<div id="frontPage">
<div className="frontPage">
<h1>Free Programming Books</h1>
{/* <input type="text"></input> */}
<SearchBar setSearch={setSearch}/>
<LangDropdown data={data}/>
<SubmitButton/>
<ol>
<div>
<SearchBar changeParameter={changeParameter}/>
<LangDropdown changeParameter={changeParameter} data={data}/>
</div>
<h2>Section Results</h2>
<div className="search-results">
{sectionResults}
</div>
<h2>Top Results</h2>
<div className="search-results">
{resultsList}
</ol>
</div>
</div>
);

View File

@ -1,17 +1,23 @@
import React, { useState, useEffect } from 'react';
function LangDropdown({ data }){
function LangDropdown({ changeParameter, data }){
const [ languages, setLanguages ] = useState([]);
let options = null;
const handleChange = (e) => {
changeParameter('lang.code', e.target.value);
}
useEffect( // run whenever data changes
() => {
if(data){
let langArray = [];
data.children[0].children.forEach( (document) => {
langArray.push(document.language);
});
langArray.sort((a, b) => a.name > b.name)
setLanguages(langArray);
}
},
[data]
)
@ -27,9 +33,8 @@ function LangDropdown({ data }){
});
// console.log(options);
return(
<select name="languages" id="languages">
<option key="allLangs" value="allLangs">All Languages</option>
<select onChange={handleChange} name="languages" id="languages">
<option key="allLangs" value="">All Languages</option>
{options}
</select>
)

View File

@ -2,7 +2,7 @@ import React from 'react';
function SearchBar(props){
const handleChange = (e) => {
props.setSearch(e.target.value);
props.changeParameter('title', e.target.value);
}
return(

View File

@ -0,0 +1,14 @@
import React, { useState, useEffect } from 'react';
function SearchResult({ data }){
return(
<div>
<button>
<h3>{data.title} by {data.author}</h3>
<p>({data.lang.code})</p>
</button>
</div>
)
}
export default SearchResult;