Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Обратная сторона реальности #4

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions src/components/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,30 @@ import NotFoundPage from '../../pages/not-found-page';
import PrivateRoute from '../private-route/private-route';
import { Route, BrowserRouter, Routes } from 'react-router-dom';
import { AppRoute, AuthorizationStatus } from '../../consts';
import { Offer } from '../../mocks/offers';

type AppProps = {
offersCount: number;
offers: Offer[];
};

export default function App({ offersCount }: AppProps): JSX.Element {
export default function App({ offers }: AppProps): JSX.Element {
return (
<BrowserRouter>
<Routes>
<Route
path={AppRoute.Main}
element={<MainPage offersCount={offersCount} />}
element={<MainPage offers={offers} />}
/>
<Route
path={AppRoute.Favorites}
element={
<PrivateRoute authorizationStatus={AuthorizationStatus.Auth}>
<FavoritesPage />
<FavoritesPage offers={offers}/>
</PrivateRoute>
}
/>
<Route path={AppRoute.Login} element={<LoginPage />} />
<Route path={AppRoute.Offer} element={<OfferPage />} />
<Route path={AppRoute.Offer} element={<OfferPage offers={offers} />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</BrowserRouter>
Expand Down
47 changes: 32 additions & 15 deletions src/components/card/card.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,44 @@
export default function Card() {
import { Link } from 'react-router-dom';
import { Offer } from '../../mocks/offers';

type CardProps = {
offer: Offer;
offerClassName: string;
}

const FAVORITES_CLASS_NAME = 'favorites';

export default function Card(props: CardProps) {
const {offer, offerClassName} = props;
const imgWidth = offerClassName === FAVORITES_CLASS_NAME ? 150 : 260;
const imgHeight = offerClassName === FAVORITES_CLASS_NAME ? 110 : 200;
const {isPremium, images, price, rating, title, type, id, isFavorite} = offer;

return (
<article className="cities__card place-card">
<div className="place-card__mark">
<span>Premium</span>
</div>
<div className="cities__image-wrapper place-card__image-wrapper">
<article className={`${offerClassName}__card place-card`} data-id={id}>
{isPremium && (
<div className="place-card__mark">
<span>Premium</span>
</div>
)}
<div className={`${offerClassName}__image-wrapper place-card__image-wrapper`}>
<a href="#">
<img
className="place-card__image"
src="img/apartment-01.jpg"
width="260"
height="200"
src={images[0]}
width={imgWidth}
height={imgHeight}
alt="Place image"
/>
</a>
</div>
<div className="place-card__info">
<div className={`${isFavorite ? 'favorites__card-info' : ''} place-card__info`}>
<div className="place-card__price-wrapper">
<div className="place-card__price">
<b className="place-card__price-value">&euro;120</b>
<b className="place-card__price-value">&euro;{price}</b>
<span className="place-card__price-text">&#47;&nbsp;night</span>
</div>
<button className="place-card__bookmark-button button" type="button">
<button className={`place-card__bookmark-button button ${isFavorite ? 'place-card__bookmark-button--active' : ''}`} type="button">
<svg className="place-card__bookmark-icon" width="18" height="19">
<use xlinkHref="#icon-bookmark"></use>
</svg>
Expand All @@ -30,14 +47,14 @@ export default function Card() {
</div>
<div className="place-card__rating rating">
<div className="place-card__stars rating__stars">
<span style={{ width: '80%' }}></span>
<span style={{ width: `${rating}%` }}></span>
<span className="visually-hidden">Rating</span>
</div>
</div>
<h2 className="place-card__name">
<a href="#">Beautiful &amp; luxurious apartment at great location</a>
<Link to={`/offer/${id}`}>{title}</Link>
</h2>
<p className="place-card__type">Apartment</p>
<p className="place-card__type">{type}</p>
</div>
</article>
);
Expand Down
50 changes: 50 additions & 0 deletions src/components/comment-form/comment-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useState } from 'react';
import Stars from './stars';
import { StarsValue } from './stars';

type CustomFormState = {
rating: StarsValue;
comment: string;
}

export default function CommentForm() {
const [formState, setFormState] = useState<CustomFormState>({rating: '0', comment: ''});

return (
<form className="reviews__form form" action="#" method="post">
<label
className="reviews__label form__label"
htmlFor="review"
>
Your review
</label>
<Stars value={formState.rating} onChange={(rating) => setFormState({...formState, rating})}/>
<textarea
className="reviews__textarea form__textarea"
id="review"
name="review"
placeholder="Tell how was your stay, what you like and what can be improved"
value={formState.comment}
onChange={(evt) => {
setFormState({...formState, comment: evt.target.value});
}}
>
</textarea>
<div className="reviews__button-wrapper">
<p className="reviews__help">
To submit review please make sure to set{' '}
<span className="reviews__star">rating</span> and describe
your stay with at least{' '}
<b className="reviews__text-amount">50 characters</b>.
</p>
<button
className="reviews__submit form__submit button"
type="submit"
disabled
>
Submit
</button>
</div>
</form>
);
}
40 changes: 40 additions & 0 deletions src/components/comment-form/stars.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export type StarsValue = '0' | '1' | '2' | '3' | '4' | '5';

type StarsProps = {
value: StarsValue;
onChange: (value: StarsValue) => void;
}

export default function Stars({value, onChange}: StarsProps) {
return (
<div className="reviews__rating-form form__rating">
{Array.from({ length: 5 }, (_, idx) => {
const key = String(5 - idx) as StarsValue;

return (
<>
<input
className="form__rating-input visually-hidden"
name="rating"
value={key}
id={`${key}-stars`}
type="radio"
key={key}
checked={value === key}
onChange={() => onChange(key)}
/>
<label
htmlFor={`${key}-stars`}
className="reviews__rating-label form__rating-label"
title="perfect"
>
<svg className="form__star-image" width="37" height="33">
<use xlinkHref="#icon-star"></use>
</svg>
</label>
</>
);
})}
</div>
);
}
30 changes: 30 additions & 0 deletions src/components/offers-list/offers-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { MouseEventHandler, useState } from 'react';
import { Offer } from '../../mocks/offers';
import Card from '../card/card';

type OffersListProps = {
className: string;
offers: Offer[];
offerClassName: string;
}

export default function OffersList({ className, offers, offerClassName }: OffersListProps): JSX.Element {
const setActiveCard = useState('')[1];
const onMouseMoveHandler: MouseEventHandler<HTMLElement> = (evt) => {
let id = '';
if(evt.target instanceof HTMLElement) {
const element = evt.target.closest('[data-id]');

if(element instanceof HTMLElement) {
id = element.dataset.id || '';
}
}

setActiveCard(id);
};

return (
<div className={`${className} places__list`} onMouseMove={onMouseMoveHandler}>
{offers.map((offer) => <Card offerClassName={offerClassName} offer={offer} key={offer.id}/>)}
</div>);
}
9 changes: 5 additions & 4 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './components/app/app';
import {offers} from './mocks/offers';

const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);

const Setting = {
OffercCount: 5,
} as const;
// const Setting = {
// OffercCount: 5,
// } as const;

root.render(
<React.StrictMode>
<App offersCount={Setting.OffercCount} />
<App offers={offers}/>
</React.StrictMode>
);
Loading
Loading