From f957500580b5e67f5f15994a22d584a47ea2e41b Mon Sep 17 00:00:00 2001 From: Avior Date: Thu, 15 Apr 2021 01:21:55 +0200 Subject: [PATCH] Add NotificationManager a Component that manage Notifications for you Signed-off-by: Avior --- .../NotificationManager.module.styl | 38 ++++ .../NotificationManager.stories.tsx | 18 ++ src/dzeio/NotificationManager/index.tsx | 179 ++++++++++++++++++ src/index.ts | 3 +- 4 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 src/dzeio/NotificationManager/NotificationManager.module.styl create mode 100644 src/dzeio/NotificationManager/NotificationManager.stories.tsx create mode 100644 src/dzeio/NotificationManager/index.tsx diff --git a/src/dzeio/NotificationManager/NotificationManager.module.styl b/src/dzeio/NotificationManager/NotificationManager.module.styl new file mode 100644 index 0000000..7a88f39 --- /dev/null +++ b/src/dzeio/NotificationManager/NotificationManager.module.styl @@ -0,0 +1,38 @@ +@import '../config.styl' + +.section + position fixed + bottom 0 + left 0 + padding 0 16px + max-width 25% + @media (max-width $tablet) + max-width 50% + @media (max-width $mobile) + width 100% + max-width 100% + > div + margin-bottom 16px + animation spawn 1 forwards ease-in-out .3s + + &.remove + animation despawn 1 both ease-in-out .3s + + .title + font-weight normal + margin 0 +@keyframes spawn + from + opacity 0 + transform translateY(100%) + to + opacity 1 + transform translateY(0) + +@keyframes despawn + from + opacity 1 + transform translateY(0) + to + opacity 0 + transform translateY(100%) diff --git a/src/dzeio/NotificationManager/NotificationManager.stories.tsx b/src/dzeio/NotificationManager/NotificationManager.stories.tsx new file mode 100644 index 0000000..bd168f8 --- /dev/null +++ b/src/dzeio/NotificationManager/NotificationManager.stories.tsx @@ -0,0 +1,18 @@ +import { Meta } from '@storybook/react/types-6-0' +import React from 'react' +import { Zap } from 'react-feather' +import Component from '.' + +export default { + title: 'DZEIO/NotificationManager', + component: Component +} as Meta + +export const Basic = (args: any) => +Basic.args = { + ttl: 999999999999, + notifications: [ + 'Test', + 'LArge text lorem ipsum dolor sit amet, i dont know what to type yolo :D' + ] +} \ No newline at end of file diff --git a/src/dzeio/NotificationManager/index.tsx b/src/dzeio/NotificationManager/index.tsx new file mode 100644 index 0000000..7190060 --- /dev/null +++ b/src/dzeio/NotificationManager/index.tsx @@ -0,0 +1,179 @@ +import { buildClassName } from '../Util' +import Button from '../Button' +import Box from '../Box' +import Col from '../Col' +import Text from '../Text' +import Router from 'next/router' +import React from 'react' +import { X } from 'react-feather' + +import css from './NotificationManager.module.styl' + +export interface Notification { + message: string + actions?: Array<{ + txt: string + action: (this: HTMLInputElement, event: React.MouseEvent) => void + }> + ttl?: number + internal?: { + timeRemaining: number + } +} + +interface Props { + manageRoutes?: boolean + ttl?: number + notifications?: Array +} + +interface State { + notifications: Array +} + +export default class NotificationManager extends React.Component { + + private static instance: NotificationManager + + + public state: State = { + notifications: [] + } + + private interval?: NodeJS.Timeout + private freezedNotification?: number + + public constructor(props: Props | Readonly) { + super(props) + NotificationManager.instance = this + } + + public static addNotification(notif: Omit | string): number { + const realNotif: Notification = typeof notif === 'string' ? { message: notif, ttl: this.instance.props.ttl ?? 2000 } : notif + + if (realNotif.ttl) { + realNotif.ttl /= 100 + realNotif.internal = { timeRemaining: realNotif.ttl } + } + const notifs = this.instance.state.notifications + const id = notifs.push(realNotif) - 1 + this.instance.setState({ + notifications: notifs + }) + return id + } + + public static removeNotification(id: number, array?: Array) { + const notifs = array || this.instance.state.notifications + notifs[id] = undefined + if (array) { + return + } + this.instance.setState({ + notifications: notifs + }) + } + + public componentDidMount() { + if (this.props.notifications) { + for (const notif of this.props.notifications) { + NotificationManager.addNotification(notif) + } + } + if (this.props.manageRoutes) { + Router.events.on('routeChangeComplete', this.checkRouteForMessage) + this.checkRouteForMessage() + } + } + + public componentDidUpdate() { + if (this.state.notifications.length > 0 && !this.interval) { + this.interval = setInterval(() => { + const notifs = this.state.notifications + for (let id = 0; id < notifs.length; id++) { + const notif = notifs[id] + if (this.freezedNotification === id && notif?.ttl) { + notif.internal = {timeRemaining: notif.ttl} + } + if (!notif || typeof notif.internal?.timeRemaining !== 'number' || id === this.freezedNotification) { + continue + } + if (notif.internal.timeRemaining < 1) { + NotificationManager.removeNotification(id) + } + notif.internal.timeRemaining -= 1 + } + this.setState({ + notifications: notifs + }) + }, 100) + } + if (this.state.notifications.length === 0 && this.interval) { + clearInterval(this.interval) + this.interval = undefined + } + } + + public componentWillUnMount() { + if (this.interval) { + clearInterval(this.interval) + } + } + + public render = () => ( +
+ {this.state.notifications.map((el, index) => { + if (el === undefined) { + return + } + return ( +
+ + NotificationManager.removeNotification(index)} /> + + )} + > + {el.actions && ( +
+ {el.actions.map((btn, aIndex) => ( + + ))} +
+ )} +
+
+ ) + })} +
+ ) + + private onMouseEnter = (id: number) => () => { + this.freezedNotification = id + } + + private onMouseExit = (id: number) => () => { + if (this.freezedNotification === id) { + this.freezedNotification = undefined + } + } + + private checkRouteForMessage = () => { + const msg = Router.query.msg + console.log(msg) + if (typeof msg === 'string') { + NotificationManager.addNotification(decodeURI(msg)) + } + } +} diff --git a/src/index.ts b/src/index.ts index d66402e..6036255 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,6 +20,7 @@ import Input from './dzeio/Input' import Link from './dzeio/Link' import Loader from './dzeio/Loader' import Navbar from './dzeio/Navbar' +import NotificationManager from './dzeio/NotificationManager' import Overflow from './dzeio/Overflow' import Popup from './dzeio/Popup' import Row from './dzeio/Row' @@ -41,8 +42,8 @@ export { Input, Link, Loader, - Menu, Navbar, + NotificationManager, Overflow, Popup, Row,