diff --git a/package.json b/package.json index 2b097dc1..1cc7b2bd 100755 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "@cypress/webpack-dev-server": "^1.8.4", "@mate-academy/cypress-tools": "^1.0.4", "@mate-academy/eslint-config-react-typescript": "^1.0.11", - "@mate-academy/scripts": "^1.2.3", + "@mate-academy/scripts": "^1.2.8", "@mate-academy/students-ts-config": "*", "@mate-academy/stylelint-config": "*", "@types/node": "^17.0.45", diff --git a/src/App.scss b/src/App.scss index 93f0af3a..4d26146b 100644 --- a/src/App.scss +++ b/src/App.scss @@ -2,3 +2,11 @@ iframe { display: none; } + +button { + cursor: pointer; +} + +tr { + transition: 0.3s background-color; +} diff --git a/src/App.tsx b/src/App.tsx index f6361547..e9cb3635 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,39 +1,28 @@ -import React from 'react'; +import React, { useEffect, useState } 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, setLoading] = useState(false); - render() { - return ( -
-

People table

+ useEffect(() => { + setTimeout(() => { + setLoading(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..33fd3cf8 --- /dev/null +++ b/src/components/Button/Button.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +type Props = React.ButtonHTMLAttributes & { + children: React.ReactNode; +}; + +export const Button: React.FC = ({ children, className, ...props }) => ( + +); diff --git a/src/components/Button/index.ts b/src/components/Button/index.ts new file mode 100644 index 00000000..8b166a86 --- /dev/null +++ b/src/components/Button/index.ts @@ -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..c5dfbbb9 --- /dev/null +++ b/src/components/PeopleTable/PeopleTable.tsx @@ -0,0 +1,203 @@ +import React, { useState } from 'react'; +import classNames from 'classnames'; +import { Person } from '../../types/Person'; +import { Button } from '../Button'; +import peopleFromServer from '../../people.json'; + +enum SortField { + Name = 'name', + Slug = 'slug', + Born = 'born', + Died = 'died', + Null = 'null', +} + +function sortByField(items: Person[], field: SortField) { + return [...items].sort((personA, personB) => { + switch (field) { + case SortField.Born: + case SortField.Died: + return personA[field] - personB[field]; + + case SortField.Name: + case SortField.Slug: + return personA[field].localeCompare(personB[field]); + + default: + return 0; + } + }); +} + +export const PeopleTable: React.FC<{}> = () => { + const [people, setPeople] = useState(peopleFromServer); + const [selectedPeople, setSelectedPeople] = useState([]); + const [sortField, setSortField] = useState(SortField.Null); + + const moveDown = (person: Person) => { + setPeople(currentPeople => { + const peopleCopy = [...currentPeople]; + + const personIndex = peopleCopy.findIndex( + personA => personA.slug === person.slug, + ); + + if (personIndex === peopleCopy.length - 1) { + return currentPeople; + } + + peopleCopy[personIndex + 1] = person; + peopleCopy[personIndex] = currentPeople[personIndex + 1]; + + return peopleCopy; + }); + }; + + const moveUp = (person: Person) => { + setPeople(currentPeople => { + const peopleCopy = [...currentPeople]; + + const personIndex = peopleCopy.findIndex( + personA => personA.slug === person.slug, + ); + + if (personIndex === 0) { + return currentPeople; + } + + peopleCopy[personIndex - 1] = person; + peopleCopy[personIndex] = currentPeople[personIndex - 1]; + + return peopleCopy; + }); + }; + + const selectPerson = (person: Person) => { + setSelectedPeople(currentPeople => [...currentPeople, person]); + }; + + const unselectPerson = (person: Person) => { + setSelectedPeople((currentPeople) => ( + currentPeople.filter( + selectedPerson => selectedPerson.slug !== person.slug, + ) + )); + }; + + const clearSelectedPeople = () => { + setSelectedPeople([]); + }; + + const isPersonSelected = (person: Person) => { + return selectedPeople.some( + selectedPerson => selectedPerson.slug === person.slug, + ); + }; + + const visiblePeople = sortByField(people, sortField); + + if (people.length === 0) { + return ( +

No people data yet

+ ); + } + + return ( + + + + + + + + + + + + + + {visiblePeople.map(person => ( + + + + + + + + + + ))} + +
+ {selectedPeople.length > 0 && ( +
select + name + + setSortField(SortField.Name)} + > + + + + + sexborn
+ {isPersonSelected(person) + ? ( + + ) + : ( + + )} + + {person.name} + {person.sex}{person.born} + + + +
+ ); +}; diff --git a/src/components/PeopleTable/index.ts b/src/components/PeopleTable/index.ts new file mode 100644 index 00000000..45984e7c --- /dev/null +++ b/src/components/PeopleTable/index.ts @@ -0,0 +1 @@ +export * from './PeopleTable';