diff --git a/package-lock.json b/package-lock.json index 9b870a7..20524a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "i18next-browser-languagedetector": "^6.1.3", "i18next-http-backend": "^1.3.2", "react": "^18.2.0", + "react-device-detect": "^2.2.3", "react-dom": "^18.2.0", "react-i18next": "^11.18.3", "react-redux": "^7.2.6", @@ -22735,6 +22736,18 @@ "react-dom": ">=16.8.0" } }, + "node_modules/react-device-detect": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-device-detect/-/react-device-detect-2.2.3.tgz", + "integrity": "sha512-buYY3qrCnQVlIFHrC5UcUoAj7iANs/+srdkwsnNjI7anr3Tt7UY6MqNxtMLlr0tMBied0O49UZVK8XKs3ZIiPw==", + "dependencies": { + "ua-parser-js": "^1.0.33" + }, + "peerDependencies": { + "react": ">= 0.14.0", + "react-dom": ">= 0.14.0" + } + }, "node_modules/react-docgen": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-5.4.3.tgz", @@ -26164,6 +26177,28 @@ "node": ">=4.2.0" } }, + "node_modules/ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "engines": { + "node": "*" + } + }, "node_modules/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -43243,6 +43278,14 @@ "dev": true, "requires": {} }, + "react-device-detect": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-device-detect/-/react-device-detect-2.2.3.tgz", + "integrity": "sha512-buYY3qrCnQVlIFHrC5UcUoAj7iANs/+srdkwsnNjI7anr3Tt7UY6MqNxtMLlr0tMBied0O49UZVK8XKs3ZIiPw==", + "requires": { + "ua-parser-js": "^1.0.33" + } + }, "react-docgen": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-5.4.3.tgz", @@ -45757,6 +45800,11 @@ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true }, + "ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==" + }, "uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", diff --git a/package.json b/package.json index 7914129..7981215 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,7 @@ "i18next-browser-languagedetector": "^6.1.3", "i18next-http-backend": "^1.3.2", "react": "^18.2.0", + "react-device-detect": "^2.2.3", "react-dom": "^18.2.0", "react-i18next": "^11.18.3", "react-redux": "^7.2.6", diff --git a/src/features/notificationButton/ui/NotificationButton/NotificationButton.tsx b/src/features/notificationButton/ui/NotificationButton/NotificationButton.tsx index 07814b9..f72e2e9 100644 --- a/src/features/notificationButton/ui/NotificationButton/NotificationButton.tsx +++ b/src/features/notificationButton/ui/NotificationButton/NotificationButton.tsx @@ -1,30 +1,53 @@ -import { classNames } from 'shared/lib/classNames/classNames'; -import React, { memo } from 'react'; -import { Button, ButtonTheme } from 'shared/ui/Button/Button'; -import { Icon } from 'shared/ui/Icon/Icon'; -import NotificationIcon from 'shared/assets/icons/notification-20-20.svg'; -import { NotificationList } from 'entities/Notification'; -import { Popover } from 'shared/ui/Popups'; -import cls from './NotificationButton.module.scss'; +import { classNames } from "shared/lib/classNames/classNames"; +import React, { memo, useCallback, useState } from "react"; +import { Button, ButtonTheme } from "shared/ui/Button/Button"; +import { Icon } from "shared/ui/Icon/Icon"; +import NotificationIcon from "shared/assets/icons/notification-20-20.svg"; +import { NotificationList } from "entities/Notification"; +import { Popover } from "shared/ui/Popups"; +import { Drawer } from "shared/ui/Drawer/Drawer"; +import { BrowserView, MobileView } from "react-device-detect"; +import cls from "./NotificationButton.module.scss"; interface NotificationButtonProps { - className?: string; + className?: string; } export const NotificationButton = memo((props: NotificationButtonProps) => { - const { className } = props; + const { className } = props; + const [isOpen, setIsOpen] = useState(false); - return ( + const onOpenDrawer = useCallback(() => { + setIsOpen(true); + }, []); + + const onCloseDrawer = useCallback(() => { + setIsOpen(false); + }, []); + + const trigger = ( + + ); + + return ( +
+ - - - )} + className={classNames(cls.NotificationButton, {}, [className])} + direction="bottom left" + trigger={trigger} > - + - ); + + + {trigger} + + + + +
+ ); }); diff --git a/src/shared/ui/Drawer/Drawer.module.scss b/src/shared/ui/Drawer/Drawer.module.scss new file mode 100644 index 0000000..e440393 --- /dev/null +++ b/src/shared/ui/Drawer/Drawer.module.scss @@ -0,0 +1,51 @@ +.Drawer { + position: fixed; + top: 0; + bottom: 0; + right: 0; + left: 0; + opacity: 0; + pointer-events: none; + z-index: -1; + display: flex; + align-items: flex-end; +} + +.content { + height: 70%; + background: var(--bg-color); + bottom: 0; + border-top-left-radius: 12px; + border-top-right-radius: 12px; + position: relative; + width: 100%; + min-height: 100px; + padding: 20px; + transition: 0.3s transform; + transform: translateY(100%); + overflow-y: auto; + overflow-x: hidden; + z-index: 10000; +} + +.content::before { + content: ""; + position: relative; + display: block; + width: 100px; + height: 10px; + background: var(--bg-color); + margin: auto; + bottom: 40px; + border-radius: 12px; +} + +.opened { + pointer-events: auto; + opacity: 1; + z-index: var(--modal-z-index); + + .content { + transform: translateY(0%); + } +} diff --git a/src/shared/ui/Drawer/Drawer.tsx b/src/shared/ui/Drawer/Drawer.tsx new file mode 100644 index 0000000..16b3114 --- /dev/null +++ b/src/shared/ui/Drawer/Drawer.tsx @@ -0,0 +1,40 @@ +import { classNames, Mods } from 'shared/lib/classNames/classNames'; +import React, { memo, ReactNode } from 'react'; +import { useTheme } from 'app/providers/ThemeProvider'; +import { Overlay } from '../Overlay/Overlay'; +import cls from './Drawer.module.scss'; +import { Portal } from '../Portal/Portal'; + +interface DrawerProps { + className?: string; + children: ReactNode; + isOpen?: boolean; + onClose?: () => void; +} + +export const Drawer = memo((props: DrawerProps) => { + const { + className, + children, + onClose, + isOpen, + } = props; + const { theme } = useTheme(); + + const mods: Mods = { + [cls.opened]: isOpen, + }; + + return ( + +
+ +
+ {children} +
+
+
+ ); +}); diff --git a/src/shared/ui/Modal/Modal.module.scss b/src/shared/ui/Modal/Modal.module.scss index bd7f84b..39cc79e 100644 --- a/src/shared/ui/Modal/Modal.module.scss +++ b/src/shared/ui/Modal/Modal.module.scss @@ -1,5 +1,8 @@ .Modal { position: fixed; + display: flex; + justify-content: center; + align-items: center; top: 0; bottom: 0; right: 0; diff --git a/src/shared/ui/Modal/Modal.tsx b/src/shared/ui/Modal/Modal.tsx index 441e3d8..73a2110 100644 --- a/src/shared/ui/Modal/Modal.tsx +++ b/src/shared/ui/Modal/Modal.tsx @@ -1,15 +1,17 @@ -import {classNames, Mods} from "shared/lib/classNames/classNames"; +import { classNames, Mods } from "shared/lib/classNames/classNames"; import { ReactNode, useCallback, useEffect, useRef, useState, - MouseEvent, MutableRefObject, + MouseEvent, + MutableRefObject, } from "react"; import { Portal } from "../Portal/Portal"; import { useTheme } from "app/providers/ThemeProvider"; import cls from "./Modal.module.scss"; +import { Overlay } from "../Overlay/Overlay"; interface ModalProps { className?: string; @@ -84,10 +86,9 @@ export const Modal = (props: ModalProps) => {
-
-
- {children} -
+ +
+ {children}
diff --git a/src/shared/ui/Overlay/Overlay.module.scss b/src/shared/ui/Overlay/Overlay.module.scss new file mode 100644 index 0000000..ff7a032 --- /dev/null +++ b/src/shared/ui/Overlay/Overlay.module.scss @@ -0,0 +1,13 @@ +.Overlay { + background: var(--overlay-color); + display: flex; + align-items: center; + justify-content: center; + position: fixed; + top: 0; + bottom: 0; + right: 0; + left: 0; + z-index: -1; + cursor: pointer; +} diff --git a/src/shared/ui/Overlay/Overlay.tsx b/src/shared/ui/Overlay/Overlay.tsx new file mode 100644 index 0000000..aa3164f --- /dev/null +++ b/src/shared/ui/Overlay/Overlay.tsx @@ -0,0 +1,16 @@ +import { classNames } from 'shared/lib/classNames/classNames'; +import { memo } from 'react'; +import cls from './Overlay.module.scss'; + +interface OverlayProps { + className?: string; + onClick?: () => void; +} + +export const Overlay = memo((props: OverlayProps) => { + const { className, onClick } = props; + + return ( +
+ ); +}); diff --git a/src/shared/ui/Stack/VStack/VStack.tsx b/src/shared/ui/Stack/VStack/VStack.tsx index a266628..94c6f7a 100644 --- a/src/shared/ui/Stack/VStack/VStack.tsx +++ b/src/shared/ui/Stack/VStack/VStack.tsx @@ -1,10 +1,8 @@ -import { Flex, FlexProps } from '../Flex/Flex'; +import { Flex, FlexProps } from "../Flex/Flex"; -type VStackProps = Omit +type VStackProps = Omit; export const VStack = (props: VStackProps) => { - const { align = 'start' } = props; - return ( - - ); + const { align = "start" } = props; + return ; }; diff --git a/src/widgets/SideBar/ui/SideBar/SideBar.module.scss b/src/widgets/SideBar/ui/SideBar/SideBar.module.scss index 297bd87..3bf782d 100644 --- a/src/widgets/SideBar/ui/SideBar/SideBar.module.scss +++ b/src/widgets/SideBar/ui/SideBar/SideBar.module.scss @@ -10,7 +10,7 @@ position: absolute; right: -32px; bottom: 32px; - z-index: 100000; + z-index: 10; } .switchers {