diff --git a/country-explorer/src/components/App.tsx b/country-explorer/src/components/App.tsx index 0415156..8bd2705 100644 --- a/country-explorer/src/components/App.tsx +++ b/country-explorer/src/components/App.tsx @@ -1,9 +1,11 @@ import './App.css'; import { useState } from 'react'; import { useCountries } from '../hooks/useCountries'; +import { sortCountries, type SortOption } from '../utils/sortCountries'; import CountryCard from './CountryCard'; import CountrySelector from './CountrySelector'; import RegionFilter from './RegionFilter'; +import SortSelector from './SortSelector'; import Loading from './Loading'; import ErrorMessage from './ErrorMessage'; import NavigationControls from './NavigationControls'; @@ -22,21 +24,24 @@ function App() { const filteredCountries = selectedRegion ? countries.filter((country) => getRegion(country) === selectedRegion) : countries; + const [sortOption, setSortOption] = useState('name-asc'); + // A fresh, sorted copy — filteredCountries (and the cached query data it + // derives from) is left untouched. + const sortedCountries = sortCountries(filteredCountries, sortOption); const [selectedCountryCode, setSelectedCountryCode] = useState(null); const selectedCountry = - filteredCountries.find((country) => country.code === selectedCountryCode) ?? - filteredCountries[0]; + sortedCountries.find((country) => country.code === selectedCountryCode) ?? sortedCountries[0]; const activeCountryCode = selectedCountry?.code ?? null; - const activeCountryIndex = filteredCountries.findIndex( + const activeCountryIndex = sortedCountries.findIndex( (country) => country.code === activeCountryCode, ); function navigateCountry(direction: -1 | 1) { setSelectedCountryCode((currentCode) => { - const currentIndex = filteredCountries.findIndex((country) => country.code === currentCode); - if (currentIndex === -1) return filteredCountries[0]?.code ?? null; + const currentIndex = sortedCountries.findIndex((country) => country.code === currentCode); + if (currentIndex === -1) return sortedCountries[0]?.code ?? null; - return filteredCountries[currentIndex + direction]?.code ?? currentCode; + return sortedCountries[currentIndex + direction]?.code ?? currentCode; }); } @@ -56,15 +61,19 @@ function App() {

Filters and selection

- {/* SortSelector goes here */} + navigateCountry(-1)} onNext={() => navigateCountry(1)} /> diff --git a/country-explorer/src/components/SortSelector.css b/country-explorer/src/components/SortSelector.css new file mode 100644 index 0000000..e823c24 --- /dev/null +++ b/country-explorer/src/components/SortSelector.css @@ -0,0 +1,24 @@ +.sort-selector { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 0.5rem; + margin: 1rem; +} + +.sort-selector select { + min-width: 0; + max-width: 100%; + padding: 0.5rem; + font: inherit; + color: var(--text-h); + background: var(--bg); + border: 1px solid var(--border); + border-radius: 4px; +} + +.sort-selector select:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 3px; +} diff --git a/country-explorer/src/components/SortSelector.tsx b/country-explorer/src/components/SortSelector.tsx new file mode 100644 index 0000000..054c69e --- /dev/null +++ b/country-explorer/src/components/SortSelector.tsx @@ -0,0 +1,33 @@ +import { useId } from 'react'; +import { SORT_OPTIONS, type SortOption } from '../utils/sortCountries'; +import './SortSelector.css'; + +interface SortSelectorProps { + sortOption: SortOption; + onSortChange: (option: SortOption) => void; + disabled?: boolean; +} + +function SortSelector({ sortOption, onSortChange, disabled = false }: SortSelectorProps) { + const selectId = useId(); + + return ( +
+ + +
+ ); +} + +export default SortSelector; diff --git a/country-explorer/src/utils/sortCountries.ts b/country-explorer/src/utils/sortCountries.ts new file mode 100644 index 0000000..336055c --- /dev/null +++ b/country-explorer/src/utils/sortCountries.ts @@ -0,0 +1,31 @@ +import type { Country } from '../types/country'; + +export type SortOption = 'name-asc' | 'population-desc'; + +// Drives both the