Skip to content
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
8 changes: 8 additions & 0 deletions src/components/arrow-button/ArrowButton.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@
transition: transform 0.5s ease;
}

.container:hover {
background-color: #ffc802;
}

.container:active {
background-color: #787878;
}

.container_open {
transform: translate(616px);
}
Expand Down
17 changes: 14 additions & 3 deletions src/components/arrow-button/ArrowButton.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
import arrow from 'src/images/arrow.svg';

import styles from './ArrowButton.module.scss';
import clsx from 'clsx';

/** Функция для обработки открытия/закрытия формы */
export type OnClick = () => void;

export const ArrowButton = () => {
export type ArrowButtonProps = {
isOpen?: boolean;
onClick?: OnClick;
};

export const ArrowButton = ({ isOpen, onClick }: ArrowButtonProps) => {
return (
/* Не забываем указаывать role и aria-label атрибуты для интерактивных элементов */
<div
role='button'
aria-label='Открыть/Закрыть форму параметров статьи'
tabIndex={0}
className={styles.container}>
<img src={arrow} alt='иконка стрелочки' className={styles.arrow} />
className={clsx(styles.container, { [styles.container_open]: isOpen })}
onClick={onClick}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Отлично! Круто, что вы используете clsx

<img
src={arrow}
alt='иконка стрелочки'
className={clsx(styles.arrow, { [styles.arrow_open]: isOpen })}
/>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
width: 616px;
height: 100%;
overflow: scroll;
background-color: #ffffff;
transform: translate(-616px);
transition: transform 0.5s ease;
}
Expand All @@ -16,6 +17,7 @@
display: flex;
flex-shrink: 0;
flex-direction: column;
gap: 50px;
box-sizing: border-box;
width: 616px;
height: auto;
Expand Down
176 changes: 166 additions & 10 deletions src/components/article-params-form/ArticleParamsForm.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,176 @@
import { ArrowButton } from 'components/arrow-button';
import { Button } from 'components/button';
import { Text } from 'components/text';
import { Select } from 'components/select';
import { RadioGroup } from 'components/radio-group';
import { Separator } from 'components/separator';

import styles from './ArticleParamsForm.module.scss';
import { SyntheticEvent, useEffect, useRef, useState } from 'react';
import clsx from 'clsx';
import {
ArticleStateType,
fontFamilyOptions,
fontSizeOptions,
fontColors,
contentWidthArr,
} from '../../constants/articleProps';

export type ArticleParamsFormProps = {
defaultArticleParams: ArticleStateType;
articleParams: ArticleStateType;
setCurrentArticleState: (params: ArticleStateType) => void;
};

export const ArticleParamsForm = ({
defaultArticleParams,
articleParams,
setCurrentArticleState,
}: ArticleParamsFormProps) => {
// Храним статус открытия формы
const [open, setOpen] = useState(false);

// Закрытие формы при клике вне её области и нажатию на Esc
const refForm = useRef<HTMLDivElement | null>(null);

// Открытие формы
function handlerOpenForm() {
setOpen(!open);
}

useEffect(() => {
if (!open) return;

function handleClick(event: MouseEvent) {
if (
open &&
refForm.current &&
!refForm.current.contains(event.target as HTMLElement)
) {
setOpen(!open);
}
}

function handlePressEsc(event: KeyboardEvent) {
if (event.key === 'Escape') {
setOpen(!open);
}
}

document.addEventListener('mousedown', handleClick);
document.addEventListener('keydown', handlePressEsc);

return () => {
document.removeEventListener('mousedown', handleClick);
document.removeEventListener('keydown', handlePressEsc);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Отлично! Хорошая реализация форм, круто, что вы очищаете обработчик

}, [open]);

// Стейты элементов формы
const [currentFontFamily, setCurrentFontFamily] = useState(
articleParams.fontFamilyOption
);
const [currentFontSize, setCurrentFontSize] = useState(
articleParams.fontSizeOption
);
const [currentFontColor, setCurrentFontColor] = useState(
articleParams.fontColor
);
const [currentBackgroundColor, setCurrentBackgroundColor] = useState(
articleParams.backgroundColor
);
const [currentContentWidth, setCurrentContentWidth] = useState(
articleParams.contentWidth
);

// Отправка формы
function handlerSubmitForm(event: SyntheticEvent) {
event.preventDefault();

// Изменённые данные формы для передачи в стейт
const formData: ArticleStateType = {
fontFamilyOption: currentFontFamily,
fontColor: currentFontColor,
backgroundColor: currentBackgroundColor,
contentWidth: currentContentWidth,
fontSizeOption: currentFontSize,
};

// Отправка данных в стейт
setCurrentArticleState(formData);
}

// Сброс данных формы и статьи на дефолтные
function handlerResetForm() {
setCurrentFontFamily(defaultArticleParams.fontFamilyOption);
setCurrentFontSize(defaultArticleParams.fontSizeOption);
setCurrentFontColor(defaultArticleParams.fontColor);
setCurrentBackgroundColor(defaultArticleParams.backgroundColor);
setCurrentContentWidth(defaultArticleParams.contentWidth);
setCurrentArticleState(defaultArticleParams);
}

export const ArticleParamsForm = () => {
return (
<>
<ArrowButton />
<aside className={styles.container}>
<form className={styles.form}>
<div className={styles.bottomContainer}>
<Button title='Сбросить' type='reset' />
<Button title='Применить' type='submit' />
</div>
</form>
</aside>
{/* Доп. див для правки бага двойного клика https://app.pachca.com/chats?thread_id=3492502 */}
<div ref={refForm}>
<ArrowButton onClick={handlerOpenForm} isOpen={open} />
<aside
className={clsx(styles.container, { [styles.container_open]: open })}>
<form className={styles.form} onSubmit={handlerSubmitForm}>
<Text as={'h2'} weight={800} size={31} uppercase>
Задайте параметры
</Text>

<Select
title={'шрифт'}
options={fontFamilyOptions}
selected={currentFontFamily}
onChange={setCurrentFontFamily}
/>

<RadioGroup
title={'размер шрифта'}
options={fontSizeOptions}
selected={currentFontSize}
name={'fonst-size'}
onChange={setCurrentFontSize}
/>

<Select
title={'цвет шрифта'}
options={fontColors}
selected={currentFontColor}
onChange={setCurrentFontColor}
/>

<Separator />

<Select
title={'цвет фона'}
options={fontColors}
selected={currentBackgroundColor}
onChange={setCurrentBackgroundColor}
/>

<Select
title={'цвет контекта'}
options={contentWidthArr}
selected={currentContentWidth}
onChange={setCurrentContentWidth}
/>

<div className={styles.bottomContainer}>
<Button
title='Сбросить'
type='reset'
onClick={handlerResetForm}
/>
<Button title='Применить' type='submit' />
</div>
</form>
</aside>
</div>
</>
);
};
24 changes: 24 additions & 0 deletions src/components/button/Button.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,27 @@
.button:hover {
background: var(--gold, #ffc802);
}

.button_reset {
background-color: #ffffff;
}

.button_reset:hover {
background-color: #000000;
}

.button_reset:hover > div {
color: #ffffff;
}

.button_submit {
background-color: #ffc802;
}

.button_submit:hover {
background-color: #ffedab;
}

.button_submit:hover > div {
color: #000000;
}
10 changes: 9 additions & 1 deletion src/components/button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Text } from 'components/text';

import styles from './Button.module.scss';
import clsx from 'clsx';

export const Button = ({
title,
Expand All @@ -12,7 +13,14 @@ export const Button = ({
type?: React.ButtonHTMLAttributes<HTMLButtonElement>['type'];
}) => {
return (
<button className={styles.button} type={type} onClick={onClick}>
<button
className={clsx(
styles.button,
{ [styles.button_submit]: type === 'submit' },
{ [styles.button_reset]: type === 'reset' }
)}
type={type}
onClick={onClick}>
<Text weight={800} uppercase>
{title}
</Text>
Expand Down
2 changes: 1 addition & 1 deletion src/components/radio-group/RadioGroup.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

.label:hover,
.item[data-checked='true'] .label:hover {
background: var(--gold, #ffc802);
background: var(--gold, #ffedab);
}

.item[data-checked='true'] .label {
Expand Down
8 changes: 6 additions & 2 deletions src/components/select/Select.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

.selectWrapper:not([data-is-active='true'])
.placeholder:not([data-status='invalid']):hover {
outline: 3px solid #000000;
outline: 1px solid #ffc802;
}

.select {
Expand Down Expand Up @@ -78,7 +78,11 @@
}

.option:hover {
background: var(--grey, #c4c4c4);
background: var(--grey, #eeeeee);
}

.option:active {
background: var(--grey, #d7d7d7);
}

@mixin option-color-before {
Expand Down
2 changes: 1 addition & 1 deletion src/components/separator/index.module.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.separator {
width: 100%;
height: 1px;
background: #000000;
background: #d7d7d7;
}
22 changes: 15 additions & 7 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createRoot } from 'react-dom/client';
import { StrictMode, CSSProperties } from 'react';
import { StrictMode, CSSProperties, useState } from 'react';
import clsx from 'clsx';

import { Article } from './components/article/Article';
Expand All @@ -13,19 +13,27 @@ const domNode = document.getElementById('root') as HTMLDivElement;
const root = createRoot(domNode);

const App = () => {
// Текущие параметры статьи. По умолчанию заполняем дефолтными значениями
const [currentArticleState, setCurrentArticleState] =
useState(defaultArticleState);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно лучше: компонент app лучше вынести в отдельную папку


return (
<div
className={clsx(styles.main)}
style={
{
'--font-family': defaultArticleState.fontFamilyOption.value,
'--font-size': defaultArticleState.fontSizeOption.value,
'--font-color': defaultArticleState.fontColor.value,
'--container-width': defaultArticleState.contentWidth.value,
'--bg-color': defaultArticleState.backgroundColor.value,
'--font-family': currentArticleState.fontFamilyOption.value,
'--font-size': currentArticleState.fontSizeOption.value,
'--font-color': currentArticleState.fontColor.value,
'--container-width': currentArticleState.contentWidth.value,
'--bg-color': currentArticleState.backgroundColor.value,
} as CSSProperties
}>
<ArticleParamsForm />
<ArticleParamsForm
defaultArticleParams={defaultArticleState}
articleParams={currentArticleState}
setCurrentArticleState={setCurrentArticleState}
/>
<Article />
</div>
);
Expand Down