diff --git a/public/locales/de/definition-page.json b/public/locales/de/definition-page.json new file mode 100644 index 0000000..98cdc51 --- /dev/null +++ b/public/locales/de/definition-page.json @@ -0,0 +1,6 @@ +{ + "title": "Alle Definitionen", + "addDefinition": "Definition hinzufügen", + "emptyDefinitionMessage": "No definitions", + "relatedExamples": "Related examples" +} diff --git a/public/locales/de/popularity.json b/public/locales/de/popularity.json new file mode 100644 index 0000000..9dfa331 --- /dev/null +++ b/public/locales/de/popularity.json @@ -0,0 +1,6 @@ +{ + "popularity": "Popularität", + "high": "hoch", + "medium": "durchschnitt", + "low": "niedrig" +} diff --git a/public/locales/de/word-profile.json b/public/locales/de/word-profile.json index 6751389..989def9 100644 --- a/public/locales/de/word-profile.json +++ b/public/locales/de/word-profile.json @@ -8,6 +8,7 @@ "allAssociations": "Alle Verbände", "definitions": "Definitionen", "allDefinitions": "Alle Definitionen", + "relatedExamples": "Verwandte Beispiele", "examples": "Beispiele", "allExamples": "Alle Beispiele", "forms": "Wortformen", diff --git a/public/locales/en/definition-page.json b/public/locales/en/definition-page.json new file mode 100644 index 0000000..76b1a37 --- /dev/null +++ b/public/locales/en/definition-page.json @@ -0,0 +1,6 @@ +{ + "title": "All Definitions", + "addDefinition": "Add Definition", + "emptyDefinitionMessage": "No definitions", + "relatedExamples": "Related examples" +} diff --git a/public/locales/en/popularity.json b/public/locales/en/popularity.json new file mode 100644 index 0000000..7c165a5 --- /dev/null +++ b/public/locales/en/popularity.json @@ -0,0 +1,6 @@ +{ + "popularity": "Popularity", + "high": "high", + "medium": "medium", + "low": "low" +} diff --git a/public/locales/en/word-profile.json b/public/locales/en/word-profile.json index cf9b779..6986eb1 100644 --- a/public/locales/en/word-profile.json +++ b/public/locales/en/word-profile.json @@ -8,6 +8,7 @@ "allAssociations": "All associations", "definitions": "Definitions", "allDefinitions": "All definitions", + "relatedExamples": "Related examples", "examples": "Examples", "allExamples": "All examples", "forms": "Word forms", diff --git a/public/locales/ru/definition-page.json b/public/locales/ru/definition-page.json new file mode 100644 index 0000000..533a44b --- /dev/null +++ b/public/locales/ru/definition-page.json @@ -0,0 +1,9 @@ +{ + "title_one": "{{count}} определение", + "title_few": "{{count}} определения", + "title_many": "{{count}} определений", + "title_other": "{{count}} определения", + "addDefinition": "Добавить определение", + "emptyDefinitionMessage": "Нет определений", + "relatedExamples": "связанные примеры" +} diff --git a/public/locales/ru/popularity.json b/public/locales/ru/popularity.json new file mode 100644 index 0000000..bcab69d --- /dev/null +++ b/public/locales/ru/popularity.json @@ -0,0 +1,6 @@ +{ + "popularity": "Популярность", + "high": "высокая", + "medium": "средняя", + "low": "низкая" +} diff --git a/public/locales/ru/word-profile.json b/public/locales/ru/word-profile.json index b97e797..9c717ea 100644 --- a/public/locales/ru/word-profile.json +++ b/public/locales/ru/word-profile.json @@ -8,6 +8,7 @@ "allAssociations": "Все ассоциации", "definitions": "Определения", "allDefinitions": "Все определения", + "relatedExamples": "Связанные примеры", "examples": "Примеры", "allExamples": "Все примеры", "forms": "Формы слова", diff --git a/src/0_app/App.tsx b/src/0_app/App.tsx index 0dc6950..11e937e 100644 --- a/src/0_app/App.tsx +++ b/src/0_app/App.tsx @@ -1,10 +1,10 @@ -/* eslint-disable react-refresh/only-export-components */ import { Theme } from '@radix-ui/themes'; import { ThemeProvider, withProviders } from './provider'; -import { Routing } from '../1_pages'; -import { themeSelector } from '../4_entities/theme'; -import { useAppSelector } from '../5_shared/model'; +import { Routing } from '@pages/index'; +import { themeSelector } from '@entities/theme'; +import { useAppSelector } from '@shared/model'; +// eslint-disable-next-line react-refresh/only-export-components function App() { const { theme } = useAppSelector(themeSelector); @@ -17,4 +17,5 @@ function App() { ); } +// eslint-disable-next-line react-refresh/only-export-components export default withProviders(); diff --git a/src/1_pages/Definitions/DefinitionsPage.module.scss b/src/1_pages/Definitions/DefinitionsPage.module.scss new file mode 100644 index 0000000..3bbc203 --- /dev/null +++ b/src/1_pages/Definitions/DefinitionsPage.module.scss @@ -0,0 +1,21 @@ +@import '@styles/colors'; +@import '@styles/mixins/font'; + +.container { + padding-top: 37px; +} + +h1 { + @include font-source-sans(34px, $dark, 400); +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 25px; +} + +.word { + margin-bottom: 40px; +} diff --git a/src/1_pages/Definitions/DefinitionsPage.tsx b/src/1_pages/Definitions/DefinitionsPage.tsx new file mode 100644 index 0000000..990b275 --- /dev/null +++ b/src/1_pages/Definitions/DefinitionsPage.tsx @@ -0,0 +1,53 @@ +import { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useParams } from 'react-router-dom'; +import { Button } from '@shared/ui'; +import { wordApi } from '@entities/word/api/wordApi'; +import { TDefinition } from '@entities/word/model/types'; +import { WordPageStatus } from '@entities/word/ui'; +import { DefinitionsList } from '@widgets/Definitions'; +import SVGIconDefinitions from '@shared/assets/icons/sections/definitions.svg?react'; +import SVGIconPlus from '@shared/assets/icons/actions/plus.svg?react'; +import styles from './DefinitionsPage.module.scss'; + +const DefinitionPage = () => { + const { t } = useTranslation('definition-page'); + const { slugWord: wordId } = useParams(); + const [definitions, setDefinitions] = useState([]); + + useEffect(() => { + if (!wordId) return; + wordApi.getAllDefinitions(wordId).then((data: TDefinition[]) => setDefinitions(data)); + }, [wordId]); + + if (!definitions?.length) { + return ( + }> + + + + ); + } + + return ( +
+ } + > + + + + + {definitions?.length && } +
+ ); +}; + +export default DefinitionPage; diff --git a/src/1_pages/Definitions/index.ts b/src/1_pages/Definitions/index.ts new file mode 100644 index 0000000..ac27860 --- /dev/null +++ b/src/1_pages/Definitions/index.ts @@ -0,0 +1 @@ +export { default } from './DefinitionsPage'; diff --git a/src/1_pages/Word/Word.tsx b/src/1_pages/Word/Word.tsx index 602e1b7..5a3cd85 100644 --- a/src/1_pages/Word/Word.tsx +++ b/src/1_pages/Word/Word.tsx @@ -1,16 +1,36 @@ +import { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { IWord } from '@entities/word/model/types'; +import { wordApi } from '@entities/word/api/wordApi'; +import { DefinitionsWidget } from '@widgets/Definitions'; import WordInfo from '@widgets/WordMainInfo/WordInfo'; -import WordCollection from '@widgets/WordCollection'; -import Associations from '@widgets/Associations'; import Synonyms from '@widgets/SynonymsAntonyms/Synonyms'; import Antonyms from '@widgets/SynonymsAntonyms/Antonyms'; +import Associations from '@widgets/Associations'; +import WordCollection from '@widgets/WordCollection'; import styles from './Word.module.scss'; const Word = () => { + const { slugWord: wordId } = useParams(); + const [word, setWord] = useState(null); + + useEffect(() => { + if (!wordId) return; + wordApi + .getWordById(wordId) + .then((data: IWord) => + setWord((prevState) => (prevState ? { ...prevState, ...data } : data)) + ); + }, [wordId]); + return (
+ {word?.definitions && wordId && ( + + )}
diff --git a/src/1_pages/index.tsx b/src/1_pages/index.tsx index 8b2aa7e..997622e 100644 --- a/src/1_pages/index.tsx +++ b/src/1_pages/index.tsx @@ -1,8 +1,8 @@ import { lazy } from 'react'; import { Route, Routes } from 'react-router-dom'; -import MainLayout from '@widgets/MainLayout/MainLayout'; import { ABOUT_ROUTE, + ALL_DEFINITIONS, COLLECT_ROUTE, EXERCISES_ROUTE, FAVOR_ROUTE, @@ -10,11 +10,13 @@ import { MAIN_ROUTE, VOCAB_ROUTE, WORD_ROUTE, -} from '../5_shared/lib/routes'; +} from '@shared/lib/routes'; +import MainLayout from '@widgets/MainLayout/MainLayout'; const Vocabulary = lazy(() => import('@pages/Vocabulary')); const Word = lazy(() => import('@pages/Word')); -const NotFoundPage = lazy(() => import('@/1_pages/NotFoundPage/NotFoundPage')); +const NotFoundPage = lazy(() => import('@pages/NotFoundPage/NotFoundPage')); +const DefinitionsPage = lazy(() => import('@pages/Definitions')); export const Routing = () => { return ( @@ -28,6 +30,7 @@ export const Routing = () => { Languages} /> Exercises} /> } /> + } /> About} /> } /> diff --git a/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/DefinitionItem.module.scss b/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/DefinitionItem.module.scss new file mode 100644 index 0000000..990e999 --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/DefinitionItem.module.scss @@ -0,0 +1,42 @@ +@use '@styles/colors'; +@import '@styles/mixins/font'; + +.definition { + margin-bottom: 1.6rem; +} + +.inner { + display: flex; + justify-content: space-between; + align-items: center; + gap: 2.1rem; +} + +.index { + @include font-source-sans(2rem, $neutral-text-middle-color, 500, 1.5); + margin: 0; +} + +.content { + flex-grow: 1; + display: flex; + flex-direction: column; + gap: 0.7rem; + margin: 0; + @include font-source-sans(1rem, $neutral-text-main-color, 500, 1.25); +} + +.copy { + margin: 0; +} + +.translateTitle { + color: $neutrals-600; +} + +.translate { + flex-grow: 1; + font-size: 1rem; + font-weight: 400; + line-height: 1.25; +} diff --git a/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/DefinitionItem.tsx b/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/DefinitionItem.tsx new file mode 100644 index 0000000..911ed2c --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/DefinitionItem.tsx @@ -0,0 +1,41 @@ +import { Examples } from './elements/Examples'; +import { TDefinition } from '@entities/word/model/types'; +import { CopyText, Popularity, ShadowBlock } from '@shared/ui'; +import styles from './DefinitionItem.module.scss'; + +type TProps = { + item: TDefinition; + index: number; +}; + +export const DefinitionItem = ({ item, index }: TProps) => { + const { text, popularity, translation, examples } = item; + + return ( +
  • + +
    +

    {index}

    +

    + {text} + {popularity && } +

    +

    + +

    +
    + {translation && ( +
    +

    Перевод

    +

    {translation}

    +

    + +

    +
    + )} + + {examples && } +
    +
  • + ); +}; diff --git a/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/elements/Examples.module.scss b/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/elements/Examples.module.scss new file mode 100644 index 0000000..1e56a94 --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/elements/Examples.module.scss @@ -0,0 +1,34 @@ +@import '@styles/mixins/font'; + +.list { + margin-top: 0.8rem; +} + +.item { + display: flex; + gap: 2rem; + align-items: center; + justify-content: space-between; + margin-bottom: 0.75rem; + font-size: 0.875rem; + line-height: 1; +} + +.marginTop { + margin-top: 1rem; +} + +.header { + display: flex; + align-items: center; + gap: 1rem; + margin: 0; +} + +.title { + display: flex; + align-items: center; + gap: 0.3rem; + @include font-source-sans(0.625rem, $neutrals-900, 400, 0.75); + text-transform: uppercase; +} diff --git a/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/elements/Examples.tsx b/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/elements/Examples.tsx new file mode 100644 index 0000000..6c9533e --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/elements/Examples.tsx @@ -0,0 +1,32 @@ +import { useTranslation } from 'react-i18next'; +import { CopyText, ShadowBlock } from '@shared/ui'; +import SVGIconLink from '@shared/assets/icons/actions/link.svg?react'; +import SVGIconArrow from '@shared/assets/icons/arrows/expand-filled.svg?react'; +import styles from './Examples.module.scss'; + +type TExamples = { + list: string[]; +}; + +export const Examples = ({ list }: TExamples) => { + const { t } = useTranslation('definition-page'); + + return ( + +

    + + + {t('relatedExamples')} + +

    +
      + {list.map((item) => ( +
    • + {item} + +
    • + ))} +
    +
    + ); +}; diff --git a/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/index.ts b/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/index.ts new file mode 100644 index 0000000..e6ee568 --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsList/DefinitionItem/index.ts @@ -0,0 +1 @@ +export { DefinitionItem } from './DefinitionItem'; diff --git a/src/2_widgets/Definitions/DefinitionsList/DefinitionsList.module.scss b/src/2_widgets/Definitions/DefinitionsList/DefinitionsList.module.scss new file mode 100644 index 0000000..dfa2b89 --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsList/DefinitionsList.module.scss @@ -0,0 +1,6 @@ +.list { + list-style: none; + padding: 0; + margin-bottom: 1.25rem; + margin-top: 0; +} diff --git a/src/2_widgets/Definitions/DefinitionsList/DefinitionsList.tsx b/src/2_widgets/Definitions/DefinitionsList/DefinitionsList.tsx new file mode 100644 index 0000000..f17607f --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsList/DefinitionsList.tsx @@ -0,0 +1,17 @@ +import { TDefinition } from '@entities/word/model/types'; +import { DefinitionItem } from './DefinitionItem'; +import styles from './DefinitionsList.module.scss'; + +type TProps = { + items: TDefinition[]; +}; + +export const DefinitionsList = ({ items }: TProps) => { + return ( +
      + {items.map((item, index) => ( + + ))} +
    + ); +}; diff --git a/src/2_widgets/Definitions/DefinitionsList/index.ts b/src/2_widgets/Definitions/DefinitionsList/index.ts new file mode 100644 index 0000000..cc05b01 --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsList/index.ts @@ -0,0 +1 @@ +export { DefinitionsList } from './DefinitionsList'; diff --git a/src/2_widgets/Definitions/DefinitionsWidget/DefinitionItem/DefinitionItem.module.scss b/src/2_widgets/Definitions/DefinitionsWidget/DefinitionItem/DefinitionItem.module.scss new file mode 100644 index 0000000..2b835f3 --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsWidget/DefinitionItem/DefinitionItem.module.scss @@ -0,0 +1,27 @@ +@use '@styles/colors'; +@import '@styles/mixins/font'; + +.item { + display: flex; + justify-content: space-between; + align-items: center; + gap: 1.8rem; + margin-bottom: 0.8rem; +} + +.index { + @include font-source-sans(2rem, $neutral-text-middle-color, 500, 1.5); + margin: 0; +} + +.definition { + flex-grow: 1; + @include font-source-sans(1rem, $neutral-text-main-color, 400, 1.25); + display: flex; + flex-direction: column; + gap: 0.6rem; +} + +.copy { + margin: 0; +} diff --git a/src/2_widgets/Definitions/DefinitionsWidget/DefinitionItem/DefinitionItem.tsx b/src/2_widgets/Definitions/DefinitionsWidget/DefinitionItem/DefinitionItem.tsx new file mode 100644 index 0000000..01eed54 --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsWidget/DefinitionItem/DefinitionItem.tsx @@ -0,0 +1,25 @@ +import { CopyText, Popularity } from '@shared/ui'; +import { TDefinition } from '@entities/word/model/types'; +import styles from './DefinitionItem.module.scss'; + +type TProps = { + item: TDefinition; + index: number; +}; + +export const DefinitionItem = ({ item, index }: TProps) => { + const { text, popularity } = item; + + return ( +
  • +

    {index}

    +
    + {text} + {popularity && } +
    +

    + +

    +
  • + ); +}; diff --git a/src/2_widgets/Definitions/DefinitionsWidget/DefinitionItem/index.ts b/src/2_widgets/Definitions/DefinitionsWidget/DefinitionItem/index.ts new file mode 100644 index 0000000..e6ee568 --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsWidget/DefinitionItem/index.ts @@ -0,0 +1 @@ +export { DefinitionItem } from './DefinitionItem'; diff --git a/src/2_widgets/Definitions/DefinitionsWidget/DefinitionsWidget.module.scss b/src/2_widgets/Definitions/DefinitionsWidget/DefinitionsWidget.module.scss new file mode 100644 index 0000000..052d8a1 --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsWidget/DefinitionsWidget.module.scss @@ -0,0 +1,9 @@ +.list { + list-style: none; + padding: 0; + margin: 0 0 0.5rem; + + & li:last-child { + margin-bottom: 0; + } +} diff --git a/src/2_widgets/Definitions/DefinitionsWidget/DefinitionsWidget.tsx b/src/2_widgets/Definitions/DefinitionsWidget/DefinitionsWidget.tsx new file mode 100644 index 0000000..25f1b2d --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsWidget/DefinitionsWidget.tsx @@ -0,0 +1,34 @@ +import { useTranslation } from 'react-i18next'; +import { DefinitionItem } from './DefinitionItem'; +import { ShadowBlockHeader } from '@entities/word/ui'; +import { ALL_DEFINITIONS, VOCAB_ROUTE } from '@shared/lib/routes'; +import { ShadowBlock, ShadowBlockContent, ShadowBlockFooter } from '@shared/ui'; +import type { TDefinition } from '@entities/word/model/types'; +import styles from './DefinitionsWidget.module.scss'; + +type TProps = { + count: string; + items: TDefinition[]; + wordId: string; +}; + +export const DefinitionsWidget = ({ count, items, wordId }: TProps) => { + const { t } = useTranslation('word-profile'); + const link = `${VOCAB_ROUTE}/${wordId}${ALL_DEFINITIONS}`; + + return ( + + + + +
      + {items.map((item, index) => ( + + ))} +
    +
    + + +
    + ); +}; diff --git a/src/2_widgets/Definitions/DefinitionsWidget/index.ts b/src/2_widgets/Definitions/DefinitionsWidget/index.ts new file mode 100644 index 0000000..9e79a15 --- /dev/null +++ b/src/2_widgets/Definitions/DefinitionsWidget/index.ts @@ -0,0 +1 @@ +export { DefinitionsWidget } from './DefinitionsWidget'; diff --git a/src/2_widgets/Definitions/index.ts b/src/2_widgets/Definitions/index.ts new file mode 100644 index 0000000..63ea12e --- /dev/null +++ b/src/2_widgets/Definitions/index.ts @@ -0,0 +1,3 @@ +export { DefinitionsWidget } from './DefinitionsWidget'; + +export { DefinitionsList } from './DefinitionsList'; diff --git a/src/2_widgets/HeaderLayout/HeaderLayout.module.scss b/src/2_widgets/HeaderLayout/HeaderLayout.module.scss index 0e3df6d..438c47a 100644 --- a/src/2_widgets/HeaderLayout/HeaderLayout.module.scss +++ b/src/2_widgets/HeaderLayout/HeaderLayout.module.scss @@ -41,6 +41,7 @@ header { span { @include font-source-sans(16px, $neutral-text-dark-color, 400, 19px, 'Fredoka'); + white-space: nowrap; } } diff --git a/src/2_widgets/MainLayout/MainLayout.module.scss b/src/2_widgets/MainLayout/MainLayout.module.scss index 8ca9bca..1c6640f 100644 --- a/src/2_widgets/MainLayout/MainLayout.module.scss +++ b/src/2_widgets/MainLayout/MainLayout.module.scss @@ -13,16 +13,9 @@ margin: 0 auto; padding: 0 215px; max-width: 1600px; - // @include media($min-width: 1750px) { - // max-width: 2190px; - // } - - // @include media($max-width: 1750px, $min-width: 1350px) { - // - // } @include media($max-width: 1350px, $min-width: 830px) { - padding: 0 115px; + padding: 0 30px 0 130px; } @include media($max-width: 830px) { diff --git a/src/2_widgets/MainLayout/MainLayout.tsx b/src/2_widgets/MainLayout/MainLayout.tsx index 2c0a7ae..6a46864 100644 --- a/src/2_widgets/MainLayout/MainLayout.tsx +++ b/src/2_widgets/MainLayout/MainLayout.tsx @@ -14,7 +14,7 @@ const MainLayout = () => {
    -
    +
    diff --git a/src/2_widgets/SynonymsAntonyms/SynonymAntonymCard/SynonymAntonymCard.tsx b/src/2_widgets/SynonymsAntonyms/SynonymAntonymCard/SynonymAntonymCard.tsx index 71ed795..836481d 100644 --- a/src/2_widgets/SynonymsAntonyms/SynonymAntonymCard/SynonymAntonymCard.tsx +++ b/src/2_widgets/SynonymsAntonyms/SynonymAntonymCard/SynonymAntonymCard.tsx @@ -21,7 +21,7 @@ export default function SynonymAntonymCard({ type = 'synonym' }: Props) { Заметка для синонима - {type === 'synonym' && } + {type === 'synonym' && } ); } diff --git a/src/2_widgets/WordLayout/WordLayout.tsx b/src/2_widgets/WordLayout/WordLayout.tsx index 2050e45..d67e0ca 100644 --- a/src/2_widgets/WordLayout/WordLayout.tsx +++ b/src/2_widgets/WordLayout/WordLayout.tsx @@ -2,7 +2,8 @@ import cx from 'classnames'; import { Link } from 'react-router-dom'; import { Word } from '@entities/words'; import { AddToFavorite } from '@features/addToFavorite'; -import { WordSubMenu, WordStatus } from '@ui/index'; +import { WordSubMenu } from '@ui/index'; +import { WordStatus } from '@entities/word/ui'; import { Carousel, Badge, CopyText } from '@shared/ui'; import styles from './WordLayout.module.scss'; diff --git a/src/2_widgets/WordMainInfo/WordInfo/WordInfo.tsx b/src/2_widgets/WordMainInfo/WordInfo/WordInfo.tsx index dbcccac..7e866f7 100644 --- a/src/2_widgets/WordMainInfo/WordInfo/WordInfo.tsx +++ b/src/2_widgets/WordMainInfo/WordInfo/WordInfo.tsx @@ -1,20 +1,20 @@ +import { Activity } from '@entities/words'; +import { WordOriginal } from './WordOriginal'; +import { Badge, Carousel } from '@ui/index'; import WordInfoUser from '../WordInfo/WordInfoUser'; -import WordOriginal from './WordOriginal'; import WordTranslation from './WordTranslation'; -import { Badge, Carousel } from '@ui/index'; import image1 from '@assets/mock-for-card.png'; import image2 from '@assets/mock-for-card-2.png'; import styles from './WordInfo.module.scss'; -import { Activity } from '@entities/words'; export interface IWord { - img: Array; + img?: Array; word: string; status: Activity; level: string; type: string; favorite: boolean; - translate: Array; + translate?: Array; } const info: IWord = { diff --git a/src/2_widgets/WordMainInfo/WordInfo/WordOriginal/WordOriginal.tsx b/src/2_widgets/WordMainInfo/WordInfo/WordOriginal/WordOriginal.tsx index 7db0bd0..cd9528e 100644 --- a/src/2_widgets/WordMainInfo/WordInfo/WordOriginal/WordOriginal.tsx +++ b/src/2_widgets/WordMainInfo/WordInfo/WordOriginal/WordOriginal.tsx @@ -1,13 +1,8 @@ -import { - WordStatus, - WordSubMenu, - CardContainer, - SectionTitle, - CopyText, - ButtonIcon, -} from '@ui/index'; +import cx from 'classnames'; import { IWord } from '../WordInfo'; +import { WordStatus } from '@entities/word/ui'; import { AddToFavorite } from '@features/addToFavorite'; +import { WordSubMenu, CardContainer, SectionTitle, CopyText, ButtonIcon } from '@ui/index'; import SvgLang from '@assets/icons/wordProfile/language.svg?react'; import SvgComment from '@assets/icons/wordProfile/comment.svg?react'; import SvgShare from '@assets/icons/wordProfile/arrow.svg?react'; @@ -15,11 +10,12 @@ import styles from './WordOriginal.module.scss'; interface Props { info: IWord; + classname?: string; } -export default function WordOriginal({ info }: Props) { +export function WordOriginal({ info, classname }: Props) { return ( - +
    diff --git a/src/2_widgets/WordMainInfo/WordInfo/WordOriginal/index.ts b/src/2_widgets/WordMainInfo/WordInfo/WordOriginal/index.ts index ca461dd..cdae9c1 100644 --- a/src/2_widgets/WordMainInfo/WordInfo/WordOriginal/index.ts +++ b/src/2_widgets/WordMainInfo/WordInfo/WordOriginal/index.ts @@ -1 +1 @@ -export { default } from './WordOriginal'; +export { WordOriginal } from './WordOriginal'; diff --git a/src/2_widgets/WordMainInfo/WordInfo/index.ts b/src/2_widgets/WordMainInfo/WordInfo/index.ts index 42c6fa3..5dc7ffe 100644 --- a/src/2_widgets/WordMainInfo/WordInfo/index.ts +++ b/src/2_widgets/WordMainInfo/WordInfo/index.ts @@ -1 +1,3 @@ export { default } from './WordInfo'; + +export { WordOriginal } from './WordOriginal'; diff --git a/src/2_widgets/WordsLayout/WordsLayout.module.scss b/src/2_widgets/WordsLayout/WordsLayout.module.scss index bc2874e..72cb022 100644 --- a/src/2_widgets/WordsLayout/WordsLayout.module.scss +++ b/src/2_widgets/WordsLayout/WordsLayout.module.scss @@ -51,8 +51,6 @@ } .btn { - width: 185px; - padding: 22px 15px !important; @include media($max-width: 830px) { padding: 10px 15px; } diff --git a/src/3_features/filteringWords/lib/filteringWordsByActivity.ts b/src/3_features/filteringWords/lib/filteringWordsByActivity.ts index 3941660..c8fa4ac 100644 --- a/src/3_features/filteringWords/lib/filteringWordsByActivity.ts +++ b/src/3_features/filteringWords/lib/filteringWordsByActivity.ts @@ -1,4 +1,4 @@ -import { Word, Activity } from '../../../4_entities/words'; +import { Word, Activity } from '@entities/words/model/types'; export const filteringWordsByActivity = (words: Array, activity: Activity) => { return words.filter((word) => word.activity === activity); diff --git a/src/4_entities/word/api/definitionsMock.ts b/src/4_entities/word/api/definitionsMock.ts new file mode 100644 index 0000000..34bb8d8 --- /dev/null +++ b/src/4_entities/word/api/definitionsMock.ts @@ -0,0 +1,67 @@ +import { TDefinition } from '../model/types'; + +export const definitionsMock: TDefinition[] = [ + { + id: 1245, + author: 'Mike', + text: 'Gain or acquire knowledge of or skill in (something) by study, experience, or being taught.', + translation: + 'Приобретать знания или умения в (чем-либо) путем изучения, приобретения опыта или обучения.', + popularity: 'high', + created: '2023-11-13T09:03:15.483Z', + modified: '2023-11-13T09:03:15.483Z', + }, + { + id: 224, + author: 'Jane', + text: 'To come to know by chance, or by study or other application', + translation: 'Весна', + popularity: 'medium', + created: '2023-11-13T09:03:15.483Z', + modified: '2023-11-13T09:03:15.483Z', + }, + { + id: 2247, + author: 'Iren', + text: 'To come to know by chance, or by study or other application', + translation: 'Весна', + popularity: 'low', + created: '2023-11-13T09:03:15.483Z', + modified: '2023-11-13T09:03:15.483Z', + }, + { + id: 12490, + author: 'Mike', + text: 'Spring', + translation: '', + popularity: 'high', + examples: [ + "We learn from the record kept at the Freedmen's Bureau, that there are two thousand two hundred children here", + ], + created: '2023-11-13T09:03:15.483Z', + modified: '2023-11-13T09:03:15.483Z', + }, + { + id: 2244, + author: 'Jane', + text: 'To come to know by chance, or by study or other application', + translation: 'Весна', + popularity: 'medium', + created: '2023-11-13T09:03:15.483Z', + modified: '2023-11-13T09:03:15.483Z', + }, + { + id: 22472, + author: 'Nick', + text: 'Become aware of (something) by information or from observation.', + translation: + 'Приобретать знания или умения в (чем-либо) путем изучения, приобретения опыта или обучения.', + popularity: 'low', + examples: [ + "We learn from the record kept at the Freedmen's Bureau, that there are two thousand two hundred children here", + "If you want to be more than just ordinary, then at some point, you're going to have to learn to burn your boats", + ], + created: '2023-11-13T09:03:15.483Z', + modified: '2023-11-13T09:03:15.483Z', + }, +]; diff --git a/src/4_entities/word/api/wordApi.tsx b/src/4_entities/word/api/wordApi.tsx new file mode 100644 index 0000000..3e16e5f --- /dev/null +++ b/src/4_entities/word/api/wordApi.tsx @@ -0,0 +1,29 @@ +import { IWord, TDefinition } from '../model/types'; +import { wordMock } from './wordMock'; +import { definitionsMock } from './definitionsMock'; + +export const wordApi = { + getWordById: async (id: string) => { + console.log('[wordApi]: wordId=', id); + + const response: Promise = new Promise((resolve) => { + setTimeout(() => { + resolve(wordMock); + }, 1); + }); + + return await response; + }, + + getAllDefinitions: async (id: string) => { + console.log('[wordApi]: wordId=', id); + + const response: Promise = new Promise((resolve) => { + setTimeout(() => { + resolve(definitionsMock); + }, 1); + }); + + return await response; + }, +}; diff --git a/src/4_entities/word/api/wordMock.ts b/src/4_entities/word/api/wordMock.ts new file mode 100644 index 0000000..3bb9c2a --- /dev/null +++ b/src/4_entities/word/api/wordMock.ts @@ -0,0 +1,60 @@ +import { IWord } from '../model/types'; + +export const wordMock: IWord = { + id: 1, + slug: 'word1', + created: '2022-01-01', + modified: '2022-03-02', + translations_count: 1, + favorite: false, + types: [0, 1], + text: 'textWord1', + language: 'en', + activity: 'ACTIVE', + translations: [ + { + id: 243432, + text: 'textTranslation1', + }, + ], + examples_count: 0, + examples: [], + definitions: [ + { + id: 1245, + author: 'Mike', + text: 'Become aware of (something) by information or from observation.', + translation: + 'Приобретать знания или умения в (чем-либо) путем изучения, приобретения опыта или обучения.', + popularity: 'high', + created: '2023-11-13T09:03:15.483Z', + modified: '2023-11-13T09:03:15.483Z', + }, + { + id: 224, + author: 'Jane', + text: 'To come to know by chance, or by study or other application', + translation: 'Весна', + popularity: 'medium', + created: '2023-11-13T09:03:15.483Z', + modified: '2023-11-13T09:03:15.483Z', + }, + { + id: 2247, + author: 'Iren', + text: 'Gain or acquire knowledge of or skill in (something) by study, experience, or being taught.', + translation: + 'Приобретать знания или умения в (чем-либо) путем изучения, приобретения опыта или обучения.', + popularity: 'low', + created: '2023-11-13T09:03:15.483Z', + modified: '2023-11-13T09:03:15.483Z', + }, + ], + tags: [], + is_problematic: false, + synonyms: [], + antonyms: [], + forms: [], + similars: [], + notes: [], +}; diff --git a/src/4_entities/word/model/types.ts b/src/4_entities/word/model/types.ts new file mode 100644 index 0000000..d1530f0 --- /dev/null +++ b/src/4_entities/word/model/types.ts @@ -0,0 +1,64 @@ +import { TPopularity } from '@shared/ui/Popularity/Popularity'; + +export type TTranslation = { + id: number; + text: string; +}; + +export interface ISynonym extends TTranslation {} + +export interface IAntonym extends TTranslation {} + +export interface IForm extends TTranslation {} + +export interface ISimilar extends TTranslation {} + +export type TExample = { + id: number; + text: string; + translation?: string; + created: string; + modified: string; +}; + +export type TDefinition = { + id: number; + text: string; + translation: string; + author?: string; + created: string; + examples?: Array; + modified: string; + popularity?: TPopularity; +}; + +export type TNote = { + text: string; +}; + +export type TActivity = 'INACTIVE' | 'ACTIVE' | 'MASTERED'; + +export interface IWord { + id: number; + slug: string; + language: string; + text: string; + translations: Array; + translations_count: number; + examples_count: number; + examples: Array; + definitions: Array; + types: Array; + tags: Array; + is_problematic: boolean; + activity: TActivity; + collections?: Array; + created: string; + modified: string; + synonyms: Array; + favorite: boolean; + antonyms: Array; + forms: Array; + similars: Array; + notes: Array; +} diff --git a/src/4_entities/word/ui/ShadowBlockHeader/ShadowBlockHeader.module.scss b/src/4_entities/word/ui/ShadowBlockHeader/ShadowBlockHeader.module.scss new file mode 100644 index 0000000..3beae39 --- /dev/null +++ b/src/4_entities/word/ui/ShadowBlockHeader/ShadowBlockHeader.module.scss @@ -0,0 +1,23 @@ +@use '@styles/colors' as colors; +@import '@styles/mixins/font'; + +$totalColor: colors.$neutral-text-middle-color; + +.header { + display: flex; + gap: 0.5rem; + justify-content: space-between; + align-items: center; + border-bottom: solid 1px #cdcdcd; + padding: 0.6rem 1.25rem; +} + +.total { + @include font-source-sans(1.5rem, $totalColor, 500, 1.5, 'Inter'); +} + +.actions { + display: flex; + align-items: center; + gap: 0.5rem; +} diff --git a/src/4_entities/word/ui/ShadowBlockHeader/ShadowBlockHeader.tsx b/src/4_entities/word/ui/ShadowBlockHeader/ShadowBlockHeader.tsx new file mode 100644 index 0000000..13bc78c --- /dev/null +++ b/src/4_entities/word/ui/ShadowBlockHeader/ShadowBlockHeader.tsx @@ -0,0 +1,27 @@ +import { SectionTitle, ButtonIcon } from '@shared/ui'; +import SVGPlus from '@assets/icons/actions/plus.svg?react'; +import SVGLearn from '@assets/icons/actions/hat.svg?react'; +import styles from './ShadowBlockHeader.module.scss'; + +type TWidgetHeader = { + title: string; + count: string; +}; + +export const ShadowBlockHeader = ({ title, count }: TWidgetHeader) => { + return ( +
    + + {title} {count} + +
    + + + + + + +
    +
    + ); +}; diff --git a/src/4_entities/word/ui/ShadowBlockHeader/index.ts b/src/4_entities/word/ui/ShadowBlockHeader/index.ts new file mode 100644 index 0000000..025677c --- /dev/null +++ b/src/4_entities/word/ui/ShadowBlockHeader/index.ts @@ -0,0 +1 @@ +export { ShadowBlockHeader } from './ShadowBlockHeader'; diff --git a/src/4_entities/word/ui/WordPageStatus/WordPageStatus.module.scss b/src/4_entities/word/ui/WordPageStatus/WordPageStatus.module.scss new file mode 100644 index 0000000..049d5c6 --- /dev/null +++ b/src/4_entities/word/ui/WordPageStatus/WordPageStatus.module.scss @@ -0,0 +1,11 @@ +.header { + display: flex; + justify-content: space-between; + margin-bottom: 2rem; + padding: 1.5rem 1.4rem; +} + +.actions { + display: flex; + gap: 0.6rem; +} diff --git a/src/4_entities/word/ui/WordPageStatus/WordPageStatus.tsx b/src/4_entities/word/ui/WordPageStatus/WordPageStatus.tsx new file mode 100644 index 0000000..a9470b9 --- /dev/null +++ b/src/4_entities/word/ui/WordPageStatus/WordPageStatus.tsx @@ -0,0 +1,20 @@ +import { ReactElement, ReactNode } from 'react'; +import { SectionTitle, ShadowBlock } from '@shared/ui'; +import styles from './WordPageStatus.module.scss'; + +type TWordPageStatus = { + title: string; + icon: ReactElement; + children: ReactNode | ReactNode[]; +}; + +export const WordPageStatus = ({ title, icon, children }: TWordPageStatus) => { + return ( + + + {icon} {title} + +
    {children}
    +
    + ); +}; diff --git a/src/4_entities/word/ui/WordPageStatus/index.ts b/src/4_entities/word/ui/WordPageStatus/index.ts new file mode 100644 index 0000000..cd3f4a7 --- /dev/null +++ b/src/4_entities/word/ui/WordPageStatus/index.ts @@ -0,0 +1 @@ +export { WordPageStatus } from './WordPageStatus'; diff --git a/src/5_shared/ui/WordStatus/WordStatus.module.scss b/src/4_entities/word/ui/WordStatus/WordStatus.module.scss similarity index 100% rename from src/5_shared/ui/WordStatus/WordStatus.module.scss rename to src/4_entities/word/ui/WordStatus/WordStatus.module.scss diff --git a/src/5_shared/ui/WordStatus/WordStatus.tsx b/src/4_entities/word/ui/WordStatus/WordStatus.tsx similarity index 92% rename from src/5_shared/ui/WordStatus/WordStatus.tsx rename to src/4_entities/word/ui/WordStatus/WordStatus.tsx index 09c55a2..ac81503 100644 --- a/src/5_shared/ui/WordStatus/WordStatus.tsx +++ b/src/4_entities/word/ui/WordStatus/WordStatus.tsx @@ -1,4 +1,4 @@ -import { Activity } from '@entities/words'; +import { TActivity } from '@entities/word/model/types'; import SvgStatusActive from '@shared/assets/icons/status_active.svg?react'; import SvgStatusNotActive from '@shared/assets/icons/icon_not_active.svg?react'; import SvgStatusCompleted from '@shared/assets/icons/icon_completed.svg?react'; @@ -6,7 +6,7 @@ import SvgIconQuestion from '@shared/assets/icons/icon_question.svg?react'; import styles from './WordStatus.module.scss'; type Props = { - activity?: Activity; + activity?: TActivity; colorTheme: 'dark' | 'light'; size?: 'mini' | 'big'; }; diff --git a/src/4_entities/word/ui/WordStatus/index.ts b/src/4_entities/word/ui/WordStatus/index.ts new file mode 100644 index 0000000..04cdc52 --- /dev/null +++ b/src/4_entities/word/ui/WordStatus/index.ts @@ -0,0 +1 @@ +export { WordStatus } from './WordStatus'; diff --git a/src/4_entities/word/ui/index.ts b/src/4_entities/word/ui/index.ts new file mode 100644 index 0000000..c4cee1b --- /dev/null +++ b/src/4_entities/word/ui/index.ts @@ -0,0 +1,5 @@ +export { WordStatus } from './WordStatus'; + +export { WordPageStatus } from './WordPageStatus'; + +export { ShadowBlockHeader } from './ShadowBlockHeader'; diff --git a/src/4_entities/words/index.ts b/src/4_entities/words/index.ts index 9cd8140..00236c9 100644 --- a/src/4_entities/words/index.ts +++ b/src/4_entities/words/index.ts @@ -1,2 +1,2 @@ -export type { Word, Activity, ActivityFilter } from './model/types'; +export type { Word, ActivityFilter, Activity } from './model/types'; export { wordsReducer, wordsSelector, getWords, setFilter } from './model/slice'; diff --git a/src/5_shared/assets/icons/actions/hat.svg b/src/5_shared/assets/icons/actions/hat.svg new file mode 100644 index 0000000..6e85a5d --- /dev/null +++ b/src/5_shared/assets/icons/actions/hat.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/5_shared/assets/icons/actions/plus.svg b/src/5_shared/assets/icons/actions/plus.svg index 24a8107..7743506 100644 --- a/src/5_shared/assets/icons/actions/plus.svg +++ b/src/5_shared/assets/icons/actions/plus.svg @@ -1,3 +1,3 @@ - - + + diff --git a/src/5_shared/assets/icons/icon_definition.svg b/src/5_shared/assets/icons/icon_definition.svg new file mode 100644 index 0000000..0207eed --- /dev/null +++ b/src/5_shared/assets/icons/icon_definition.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/5_shared/assets/icons/icon_related.svg b/src/5_shared/assets/icons/icon_related.svg new file mode 100644 index 0000000..c736ac8 --- /dev/null +++ b/src/5_shared/assets/icons/icon_related.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/5_shared/lib/routes.ts b/src/5_shared/lib/routes.ts index 6606e93..c8980ab 100644 --- a/src/5_shared/lib/routes.ts +++ b/src/5_shared/lib/routes.ts @@ -16,6 +16,7 @@ export const VOCAB_TROHIES_ROUTE = '/vocabulary/trophies'; export const VOCAB_WORD_ROUTE = '/vocabulary/word'; export const WORD_ROUTE = '/vocabulary/:slugWord'; +export const ALL_DEFINITIONS = '/definitions'; export const CONTACTS_ROUTE = '/contacts'; export const POLICY_ROUTE = '/privacy-policy'; diff --git a/src/5_shared/styles/colors.scss b/src/5_shared/styles/colors.scss index 7a8d4e6..4418b05 100644 --- a/src/5_shared/styles/colors.scss +++ b/src/5_shared/styles/colors.scss @@ -24,3 +24,19 @@ $success-dark-color: #2fbc48; $danger-color: #ca2744; $danger-bright-color: #ff384f; $warning-color: #ffaf05; + +$dark: #000000; + +/********************************************/ + +$primary-100: #e1eaff; +$primary-300: #b3c3fd; +$primary-400: #9eb4ff; +$primary-500: #6c8dff; +$primary-900: #14227a; + +$neutrals-100: #ffffff; +$neutrals-200: #fafafa; +$neutrals-500: #cdcdcd; +$neutrals-600: #737782; +$neutrals-900: #272932; diff --git a/src/5_shared/styles/mixins/btn.scss b/src/5_shared/styles/mixins/btn.scss index 631f1bf..40f2300 100644 --- a/src/5_shared/styles/mixins/btn.scss +++ b/src/5_shared/styles/mixins/btn.scss @@ -1,11 +1,12 @@ -@import '@styles/colors'; +@use '@styles/colors'; @import '@styles/mixins/font.scss'; @mixin btn { + display: flex; + gap: 0.34rem; box-sizing: border-box; - border: none; - border-radius: 5px; - padding: 12px 32px; + border: solid 1px transparent; + border-radius: 0.625rem; transition: color 0.2s, @@ -19,22 +20,34 @@ background-color: $neutral-background-second-color; color: $neutral-text-light-color; } + + svg { + width: 1em; + height: 1em; + } } @mixin primary-btn { @include btn; - background-color: $primary-button-color; - @include font-source-sans(12px, $neutral-text-main-color, 500); + color: colors.$neutrals-900; + background-color: colors.$primary-400; &:hover { - color: $neutral-text-contrast-color; + background-color: colors.$primary-300; } &:active, - &_active, + &:focus, &[data-state='open'] { - background-color: $primary-button-active-color; + background-color: colors.$primary-500; + color: colors.$neutrals-100; + } + + &:focus-visible { + outline: solid 2px colors.$primary-900; + background-color: colors.$primary-300; + color: colors.$neutrals-900; } } @@ -43,6 +56,20 @@ border: 1px solid $neutral-line-color; background-color: transparent; + + &:hover { + border-color: colors.$primary-300; + } + + &:active, + &:focus { + border-color: colors.$primary-500; + } + + &:focus-visible { + border-color: transparent; + outline: solid 2px colors.$primary-900; + } } @mixin secondary-btn { @@ -60,8 +87,8 @@ } @mixin tall-btn { - max-width: 216px; - height: 60px; - padding: 22px 32px; - font-size: 12px; + padding: 16px 39px; + font-size: 20px; + line-height: 24px; + font-weight: 400; } diff --git a/src/5_shared/ui/Button/Button.module.scss b/src/5_shared/ui/Button/Button.module.scss index 8601429..d298188 100644 --- a/src/5_shared/ui/Button/Button.module.scss +++ b/src/5_shared/ui/Button/Button.module.scss @@ -1,9 +1,39 @@ +@use '@styles/colors' as colors; @import '@styles/mixins/btn'; .primary { @include primary-btn; } +///////////////// size ////////////////// + +.large { + @include font-source-sans(1.25rem, colors.$neutrals-900, 400, 1.5); + padding: 1.25rem 2rem; +} + +.normal { + padding: 1rem 2rem; + @include font-source-sans(1.25rem, colors.$neutrals-900, 400, 1.5); +} + +.medium { + padding: 0.875rem 2rem; + @include font-source-sans(1rem, colors.$neutrals-900, 400, 1.25); +} + +.small { + padding: 0.75rem 1.5rem; + @include font-source-sans(0.875rem, colors.$neutrals-900, 500, 1); +} + +.micro { + padding: 0.625rem 1.5rem; + @include font-source-sans(0.75rem, colors.$neutrals-900, 500, 0.875); +} + +/////////////////////////////////////////// + .transparent { @include transparent-btn; } diff --git a/src/5_shared/ui/Button/Button.tsx b/src/5_shared/ui/Button/Button.tsx index ecb2afb..f9e5ece 100644 --- a/src/5_shared/ui/Button/Button.tsx +++ b/src/5_shared/ui/Button/Button.tsx @@ -2,7 +2,7 @@ import cx from 'classnames'; import styles from './Button.module.scss'; type ButtonTheme = 'primary' | 'transparent' | 'secondary' | 'no-border'; -type ButtonSize = 'normal' | 'tall'; +type ButtonSize = 'large' | 'normal' | 'medium' | 'small' | 'micro' | 'tall'; interface Props { children: React.ReactNode; @@ -29,7 +29,7 @@ export const Button = ({ Популярность: - - {popularity} - -
    +

    + {t('popularity')}: {t(popularity)} +

    ); } diff --git a/src/5_shared/ui/SectionTitle/SectionTitle.module.scss b/src/5_shared/ui/SectionTitle/SectionTitle.module.scss index 40ff7b4..071364e 100644 --- a/src/5_shared/ui/SectionTitle/SectionTitle.module.scss +++ b/src/5_shared/ui/SectionTitle/SectionTitle.module.scss @@ -12,6 +12,6 @@ } &_bold { - @include font-source-sans(20px, $neutral-text-dark-color, 500, 'auto', 'Inter'); + @include font-source-sans(1.25rem, $neutral-text-main-color, 500, 1.5, 'Inter'); } } diff --git a/src/5_shared/ui/ShadowBlock/ShadowBlock.module.scss b/src/5_shared/ui/ShadowBlock/ShadowBlock.module.scss new file mode 100644 index 0000000..3aa6c7e --- /dev/null +++ b/src/5_shared/ui/ShadowBlock/ShadowBlock.module.scss @@ -0,0 +1,20 @@ +@use '@styles/colors' as colors; + +.container { + border-radius: 20px; + box-shadow: + 0px 0px 8px 0px rgba(17, 17, 26, 0.1), + 0px 1px 0px 0px rgba(16, 16, 25, 0.05); +} + +.padding { + padding: 1rem; +} + +.light { + background-color: colors.$neutrals-100; +} + +.dark { + background-color: colors.$neutrals-200; +} diff --git a/src/5_shared/ui/ShadowBlock/ShadowBlock.tsx b/src/5_shared/ui/ShadowBlock/ShadowBlock.tsx new file mode 100644 index 0000000..339e6a0 --- /dev/null +++ b/src/5_shared/ui/ShadowBlock/ShadowBlock.tsx @@ -0,0 +1,25 @@ +import { ReactNode } from 'react'; +import cn from 'classnames'; +import styles from './ShadowBlock.module.scss'; + +type TShadowBlock = { + children: ReactNode; + bgColor?: 'light' | 'dark'; + withPadding?: boolean; + addClass?: string; +}; + +export const ShadowBlock = ({ + children, + bgColor = 'light', + withPadding = true, + addClass, +}: TShadowBlock) => { + return ( +
    + {children} +
    + ); +}; diff --git a/src/5_shared/ui/ShadowBlock/ShadowBlockContent/ShadowBlockContent.module.scss b/src/5_shared/ui/ShadowBlock/ShadowBlockContent/ShadowBlockContent.module.scss new file mode 100644 index 0000000..6351896 --- /dev/null +++ b/src/5_shared/ui/ShadowBlock/ShadowBlockContent/ShadowBlockContent.module.scss @@ -0,0 +1,3 @@ +.main { + padding: 1rem 1rem 1rem 1.25rem; +} diff --git a/src/5_shared/ui/ShadowBlock/ShadowBlockContent/ShadowBlockContent.tsx b/src/5_shared/ui/ShadowBlock/ShadowBlockContent/ShadowBlockContent.tsx new file mode 100644 index 0000000..ca86515 --- /dev/null +++ b/src/5_shared/ui/ShadowBlock/ShadowBlockContent/ShadowBlockContent.tsx @@ -0,0 +1,10 @@ +import { ReactNode } from 'react'; +import styles from './ShadowBlockContent.module.scss'; + +type TWidgetContent = { + children: ReactNode; +}; + +export const ShadowBlockContent = ({ children }: TWidgetContent) => { + return
    {children}
    ; +}; diff --git a/src/5_shared/ui/ShadowBlock/ShadowBlockContent/index.ts b/src/5_shared/ui/ShadowBlock/ShadowBlockContent/index.ts new file mode 100644 index 0000000..7e1adc3 --- /dev/null +++ b/src/5_shared/ui/ShadowBlock/ShadowBlockContent/index.ts @@ -0,0 +1 @@ +export { ShadowBlockContent } from './ShadowBlockContent'; diff --git a/src/5_shared/ui/ShadowBlock/ShadowBlockFooter/ShadowBlockFooter.module.scss b/src/5_shared/ui/ShadowBlock/ShadowBlockFooter/ShadowBlockFooter.module.scss new file mode 100644 index 0000000..23bf3e2 --- /dev/null +++ b/src/5_shared/ui/ShadowBlock/ShadowBlockFooter/ShadowBlockFooter.module.scss @@ -0,0 +1,20 @@ +@use '@styles/colors' as colors; +@use '@styles/mixins/font' as mixins; + +.footer { + background-color: colors.$primary-100; + border-top: solid 1px colors.$neutrals-500; + border-bottom-left-radius: 20px; + border-bottom-right-radius: 20px; + text-align: center; + padding: 0.6rem; +} + +.link { + @include mixins.font-source-sans(0.875rem, colors.$neutrals-600, 400, 1rem); + text-decoration: none; + + &:hover { + text-decoration: underline; + } +} diff --git a/src/5_shared/ui/ShadowBlock/ShadowBlockFooter/ShadowBlockFooter.tsx b/src/5_shared/ui/ShadowBlock/ShadowBlockFooter/ShadowBlockFooter.tsx new file mode 100644 index 0000000..2d8143b --- /dev/null +++ b/src/5_shared/ui/ShadowBlock/ShadowBlockFooter/ShadowBlockFooter.tsx @@ -0,0 +1,17 @@ +import { Link } from 'react-router-dom'; +import styles from './ShadowBlockFooter.module.scss'; + +type TShadowBlockFooter = { + title: string; + link: string; +}; + +export const ShadowBlockFooter = ({ title, link }: TShadowBlockFooter) => { + return ( +
    + + {title} + +
    + ); +}; diff --git a/src/5_shared/ui/ShadowBlock/ShadowBlockFooter/index.ts b/src/5_shared/ui/ShadowBlock/ShadowBlockFooter/index.ts new file mode 100644 index 0000000..842f989 --- /dev/null +++ b/src/5_shared/ui/ShadowBlock/ShadowBlockFooter/index.ts @@ -0,0 +1 @@ +export { ShadowBlockFooter } from './ShadowBlockFooter'; diff --git a/src/5_shared/ui/ShadowBlock/index.ts b/src/5_shared/ui/ShadowBlock/index.ts new file mode 100644 index 0000000..524e9a9 --- /dev/null +++ b/src/5_shared/ui/ShadowBlock/index.ts @@ -0,0 +1,5 @@ +export { ShadowBlock } from './ShadowBlock'; + +export { ShadowBlockFooter } from './ShadowBlockFooter'; + +export { ShadowBlockContent } from './ShadowBlockContent'; diff --git a/src/5_shared/ui/index.ts b/src/5_shared/ui/index.ts index 8f98740..0860080 100644 --- a/src/5_shared/ui/index.ts +++ b/src/5_shared/ui/index.ts @@ -7,7 +7,7 @@ export { Carousel } from './Carousel/Carousel'; export { Badge } from './Badge/Badge'; export { CopyText } from './CopyText/CopyText'; export { SectionTitle } from './SectionTitle/SectionTitle'; -export { WordStatus } from './WordStatus/WordStatus'; export { WordSubMenu } from './WordSubMenu/WordSubMenu'; export { CardContainer } from './CardContainer/CardContainer'; export { Popularity } from './Popularity/Popularity'; +export { ShadowBlock, ShadowBlockContent, ShadowBlockFooter } from './ShadowBlock';