diff --git a/src/App.tsx b/src/App.tsx index f6361547..b6630aaf 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,39 +1,28 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import '@fortawesome/fontawesome-free/css/all.css'; import 'bulma/css/bulma.css'; import './App.scss'; -import peopleFromServer from './people.json'; +import { Loader } from './components/Loader'; +import { PeopleTable } from './components/PeopleTable'; -export class App extends React.Component { - state = {}; +export const App: React.FC = () => { + const [isLoaded, setIsLoaded] = useState(false); - render() { - return ( -
-

People table

+ useEffect(() => { + setTimeout(() => { + setIsLoaded(true); + }, 1000); + }, []); - - - - - - - - + return ( +
+

People table

-
- {peopleFromServer.map(person => ( - - - - - - ))} - -
namesexborn
{person.name}{person.sex}{person.born}
-
- ); - } -} + {isLoaded + ? + : } + + ); +}; diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx new file mode 100644 index 00000000..c3631fe3 --- /dev/null +++ b/src/components/Button/Button.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +type Props = React.HtmlHTMLAttributes & { + children: React.ReactNode, +}; + +export const Button: React.FC = ({ children, className, ...props }) => { + return ( + + ); +}; diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx new file mode 100644 index 00000000..8b166a86 --- /dev/null +++ b/src/components/Button/index.tsx @@ -0,0 +1 @@ +export * from './Button'; diff --git a/src/components/PeopleTable/PeopleTable.tsx b/src/components/PeopleTable/PeopleTable.tsx new file mode 100644 index 00000000..2e51ce3a --- /dev/null +++ b/src/components/PeopleTable/PeopleTable.tsx @@ -0,0 +1,208 @@ +import React, { useEffect, useState } from 'react'; + +import '@fortawesome/fontawesome-free/css/all.css'; +import 'bulma/css/bulma.css'; + +import cn from 'classnames'; +import peopleFromServer from '../../people.json'; +import { Person } from '../../types/Person'; +import { Button } from '../Button'; +import { SortField } from '../../types/SortField'; + +function getReorderedPeople( + people: Person[], + sortBy: SortField, + isReversed: boolean, +) { + const peopleCopy = [...people]; + + peopleCopy.sort((personA, personB) => { + switch (sortBy) { + case SortField.Name: + return personA.name.localeCompare(personB.name); + + case SortField.Born: + return personA.born - personB.born; + + case SortField.None: + default: + return 0; + } + }); + + if (isReversed) { + peopleCopy.reverse(); + } + + return peopleCopy; +} + +export const PeopleTable: React.FC = () => { + const [people, setPeople] = useState([]); + const [selectedPeople, setSelectedPeople] = useState([]); + const [sortBy, setSortBy] = useState(SortField.None); + const [isReversed, setIsReversed] = useState(false); + + useEffect(() => { + setPeople(peopleFromServer); + }, []); + + const selectPerson = (person: Person) => { + setSelectedPeople((currentSelectedPeople) => ( + [...currentSelectedPeople, person] + )); + }; + + const unselectPerson = (person: Person) => { + setSelectedPeople((currentSelectedPeople) => ( + currentSelectedPeople.filter( + (selectedPerson) => selectedPerson.slug !== person.slug, + ))); + }; + + const isSelected = (person: Person) => { + return selectedPeople.some( + selectedPerson => selectedPerson.slug === person.slug, + ); + }; + + const changeSortType = (newSortBy: SortField) => { + const isAlreadySorted = sortBy === newSortBy; + + if (isAlreadySorted) { + setIsReversed((currentIsReversed) => !currentIsReversed); + } else { + setIsReversed(false); + setSortBy(newSortBy); + } + }; + + const visiblePeople = getReorderedPeople(people, sortBy, isReversed); + + if (visiblePeople.length === 0) { + return ( +

+ No people on server +

+ ); + } + + return ( + + + + + + + + + + + + + + + + {visiblePeople.map(person => { + const isPersonSelected = isSelected(person); + + return ( + + + + + + + ); + })} + +
+ {selectedPeople.length > 0 && ( +
- + name + { + changeSortType(SortField.Name); + }} + > + + + sex + born + { + changeSortType(SortField.Born); + }} + > + + +
+ {isPersonSelected + ? ( + + ) + : ( + + )} + + {person.name} + {person.sex}{person.born}
+ ); +}; diff --git a/src/components/PeopleTable/index.tsx b/src/components/PeopleTable/index.tsx new file mode 100644 index 00000000..45984e7c --- /dev/null +++ b/src/components/PeopleTable/index.tsx @@ -0,0 +1 @@ +export * from './PeopleTable'; diff --git a/src/types/SortField.ts b/src/types/SortField.ts new file mode 100644 index 00000000..d10c18b2 --- /dev/null +++ b/src/types/SortField.ts @@ -0,0 +1,5 @@ +export enum SortField { + None = 'none', + Name = 'name', + Born = 'born', +}