-
Notifications
You must be signed in to change notification settings - Fork 0
First commit #3
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
First commit #3
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,137 @@ | ||
| import { FormEvent, useEffect, useRef, useState } from 'react'; | ||
| import clsx from 'clsx'; | ||
| import { ArrowButton } from 'src/ui/arrow-button'; | ||
| import { Button } from 'src/ui/button'; | ||
| import { RadioGroup } from 'src/ui/radio-group'; | ||
| import { Select } from 'src/ui/select'; | ||
| import { Separator } from 'src/ui/separator'; | ||
| import { Text } from 'src/ui/text'; | ||
|
|
||
| import { | ||
| OptionType, | ||
| fontFamilyOptions, | ||
| fontColors, | ||
| backgroundColors, | ||
| contentWidthArr, | ||
| fontSizeOptions, | ||
| ArticleStateType, | ||
| } from 'src/constants/articleProps'; | ||
|
|
||
| import styles from './ArticleParamsForm.module.scss'; | ||
|
|
||
| export const ArticleParamsForm = () => { | ||
| type ArticleParamsFormProps = { | ||
| isOpen: boolean; | ||
| onToggle: () => void; | ||
| initialState: ArticleStateType; | ||
| currentState: ArticleStateType; | ||
| onApply?: (values: ArticleStateType) => void; | ||
| onReset?: (values: ArticleStateType) => void; | ||
| onClose?: () => void; | ||
| }; | ||
|
|
||
| export const ArticleParamsForm = ({ | ||
| isOpen, | ||
| onToggle, | ||
| initialState, | ||
| currentState, | ||
| onApply, | ||
| onReset, | ||
| onClose, | ||
| }: ArticleParamsFormProps) => { | ||
| const [formState, setFormState] = useState<ArticleStateType>(currentState); | ||
| const containerRef = useRef<HTMLElement | null>(null); | ||
|
|
||
| useEffect(() => { | ||
| setFormState(currentState); | ||
| }, [currentState]); | ||
|
|
||
| useEffect(() => { | ||
| if (!isOpen) { | ||
| return; | ||
| } | ||
|
|
||
| const handleClickOutside = (event: MouseEvent) => { | ||
| const root = containerRef.current; | ||
| const target = event.target as Node | null; | ||
| if (root && target && !root.contains(target)) { | ||
| onClose?.(); | ||
| } | ||
| }; | ||
|
|
||
| window.addEventListener('mousedown', handleClickOutside); | ||
|
|
||
| return () => { | ||
| window.removeEventListener('mousedown', handleClickOutside); | ||
| }; | ||
| }, [isOpen, onClose]); | ||
|
|
||
| const handleChange = | ||
| <Key extends keyof ArticleStateType>(key: Key) => | ||
| (option: OptionType) => { | ||
| setFormState((prev) => ({ | ||
| ...prev, | ||
| [key]: option, | ||
| })); | ||
| }; | ||
|
|
||
| const handleSubmit = (e: FormEvent<HTMLFormElement>) => { | ||
| e.preventDefault(); | ||
| onApply?.(formState); | ||
| }; | ||
|
|
||
| const handleReset = (e: FormEvent<HTMLFormElement>) => { | ||
| e.preventDefault(); | ||
| setFormState(initialState); | ||
| onReset?.(initialState); | ||
| }; | ||
|
|
||
| return ( | ||
| <> | ||
| <ArrowButton isOpen={false} onClick={() => {}} /> | ||
| <aside className={styles.container}> | ||
| <form className={styles.form}> | ||
| <ArrowButton isOpen={isOpen} onClick={onToggle} /> | ||
| <aside | ||
| className={clsx(styles.container, { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Отлично То что вы используете библиотеку |
||
| [styles.container_open]: isOpen, | ||
| })} | ||
| ref={containerRef}> | ||
| <form | ||
| className={styles.form} | ||
| onSubmit={handleSubmit} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Отлично Использование |
||
| onReset={handleReset}> | ||
| <Text as='h2' size={31} weight={800} uppercase> | ||
| Задайте параметры | ||
| </Text> | ||
| <Select | ||
| title='Шрифт' | ||
| options={fontFamilyOptions} | ||
| selected={formState.fontFamilyOption} | ||
| onChange={handleChange('fontFamilyOption')} | ||
| /> | ||
| <RadioGroup | ||
| title='Размер шрифта' | ||
| name='font-size' | ||
| options={fontSizeOptions} | ||
| selected={formState.fontSizeOption} | ||
| onChange={handleChange('fontSizeOption')} | ||
| /> | ||
| <Select | ||
| title='Цвет текста' | ||
| options={fontColors} | ||
| selected={formState.fontColor} | ||
| onChange={handleChange('fontColor')} | ||
| /> | ||
| <Separator /> | ||
| <Select | ||
| title='Цвет фона' | ||
| options={backgroundColors} | ||
| selected={formState.backgroundColor} | ||
| onChange={handleChange('backgroundColor')} | ||
| /> | ||
| <Select | ||
| title='Ширина контента' | ||
| options={contentWidthArr} | ||
| selected={formState.contentWidth} | ||
| onChange={handleChange('contentWidth')} | ||
| /> | ||
| <div className={styles.bottomContainer}> | ||
| <Button title='Сбросить' htmlType='reset' type='clear' /> | ||
| <Button title='Применить' htmlType='submit' type='apply' /> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,13 @@ | ||
| import { createRoot } from 'react-dom/client'; | ||
| import { StrictMode, CSSProperties } from 'react'; | ||
| import { CSSProperties, StrictMode, useState } from 'react'; | ||
| import clsx from 'clsx'; | ||
|
|
||
| import { Article } from './components/article/Article'; | ||
| import { ArticleParamsForm } from './components/article-params-form/ArticleParamsForm'; | ||
| import { defaultArticleState } from './constants/articleProps'; | ||
| import { | ||
| ArticleStateType, | ||
| defaultArticleState, | ||
| } from './constants/articleProps'; | ||
|
|
||
| import './styles/index.scss'; | ||
| import styles from './styles/index.module.scss'; | ||
|
|
@@ -13,19 +16,49 @@ const domNode = document.getElementById('root') as HTMLDivElement; | |
| const root = createRoot(domNode); | ||
|
|
||
| const App = () => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Можно лучше Будет лучше вынести компонент |
||
| const [isPanelOpen, setIsPanelOpen] = useState(false); | ||
| const [articleState, setArticleState] = | ||
| useState<ArticleStateType>(defaultArticleState); | ||
|
|
||
| const handleTogglePanel = () => { | ||
| setIsPanelOpen((prev) => !prev); | ||
| }; | ||
|
|
||
| const handleClosePanel = () => { | ||
| setIsPanelOpen(false); | ||
| }; | ||
|
|
||
| const handleApplyParams = (values: ArticleStateType) => { | ||
| setArticleState({ ...values }); | ||
| handleClosePanel(); | ||
| }; | ||
|
|
||
| const handleResetParams = (values: ArticleStateType) => { | ||
| setArticleState({ ...values }); | ||
| handleClosePanel(); | ||
| }; | ||
|
|
||
| return ( | ||
| <main | ||
| className={clsx(styles.main)} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Можно лучше Когда применяется только один CSS-класс без условий, можно не использование clsx , так как библиотека предназначена для управления условными классами. Если класс статичен и не зависит от условий, достаточно использовать обычную строку: className="my-class". Использование clsx в таких случаях добавляет ненужную зависимость и усложняет код без практической пользы. |
||
| 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': articleState.fontFamilyOption.value, | ||
| '--font-size': articleState.fontSizeOption.value, | ||
| '--font-color': articleState.fontColor.value, | ||
| '--container-width': articleState.contentWidth.value, | ||
| '--bg-color': articleState.backgroundColor.value, | ||
| } as CSSProperties | ||
| }> | ||
| <ArticleParamsForm /> | ||
| <ArticleParamsForm | ||
| isOpen={isPanelOpen} | ||
| onToggle={handleTogglePanel} | ||
| initialState={defaultArticleState} | ||
| currentState={articleState} | ||
| onApply={handleApplyParams} | ||
| onReset={handleResetParams} | ||
| onClose={handleClosePanel} | ||
| /> | ||
| <Article /> | ||
| </main> | ||
| ); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Можно лучше
Можно создать хук
useCloseOnOutsideClickOrEsc, который будет закрывать форму при клике вне её и по нажатию клавиши Escape.Например: