forked from kiworkshop/community-web
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[kiworkshop#17] Notification center 구현 (kiworkshop#20)
* [kiworkshop#17] Add a button to open a notification center * [kiworkshop#17] Add a notification center - TODO: render snackbars in the notification center * [kiworkshop#17] Notification center almost implemented. TODO: Add an animation when remove a notification * [kiworkshop#17] Add animation for removing a notification
- Loading branch information
1 parent
baf8066
commit 2a1df1d
Showing
10 changed files
with
301 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
src/common/presentation/components/atmos/NotificationCenterButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { Button, createStyles, makeStyles } from '@material-ui/core'; | ||
import { Notifications } from '@material-ui/icons'; | ||
import * as React from 'react'; | ||
import { connect } from 'react-redux'; | ||
import { bindActionCreators, Dispatch } from 'redux'; | ||
import * as snackbarModule from '../../state-module/snackbar'; | ||
|
||
const useStyles = makeStyles(createStyles({ | ||
icon: { | ||
opacity: 0.8, | ||
minWidth: 'initial' | ||
} | ||
})) | ||
|
||
interface Props { | ||
dispatchers: typeof snackbarModule | ||
} | ||
|
||
const NotificationCenterButton: React.FC<Props> = ({ dispatchers }) => { | ||
const classes = useStyles(); | ||
|
||
return <Button | ||
onClick={dispatchers.openNotificationCenter} | ||
className={classes.icon} | ||
size="small" | ||
> | ||
<Notifications /> | ||
</Button> | ||
} | ||
|
||
const mapDispatchToProps = (dispatch: Dispatch<snackbarModule.Action>) => ({ | ||
dispatchers: bindActionCreators(snackbarModule, dispatch) | ||
}) | ||
|
||
export default connect(null, mapDispatchToProps)(NotificationCenterButton); |
45 changes: 45 additions & 0 deletions
45
src/common/presentation/components/organisms/NotificationCenter/NotificationCenter.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { createStyles, Drawer, makeStyles } from '@material-ui/core'; | ||
import * as React from 'react'; | ||
import { Snackbar } from '../../../state-module/snackbar'; | ||
import RenderNotification from './RenderNotification'; | ||
|
||
const useStyles = makeStyles(createStyles({ | ||
drawerItems: { | ||
width: 340 | ||
}, | ||
paper: { | ||
background: 'rgba(70, 70, 70, 0.8)', | ||
} | ||
})) | ||
|
||
interface Props { | ||
snackbars: Snackbar[] | ||
opened: boolean | ||
handleClose(): any | ||
handleRemove(key: string): any | ||
} | ||
|
||
const NotificationCenter: React.SFC<Props> = ({ snackbars, opened, handleClose, handleRemove }) => { | ||
const classes = useStyles(); | ||
|
||
return <Drawer | ||
classes={{ | ||
paper: classes.paper | ||
}} | ||
anchor="right" | ||
open={opened} | ||
onClose={handleClose} | ||
> | ||
<div className={classes.drawerItems}> | ||
{snackbars.map(s => | ||
<RenderNotification | ||
key={s.key} | ||
snackbar={s} | ||
// tslint:disable-next-line: jsx-no-lambda | ||
handleRemove={() => handleRemove(s.key)} | ||
/>)} | ||
</div> | ||
</Drawer > | ||
} | ||
|
||
export default NotificationCenter; |
146 changes: 146 additions & 0 deletions
146
src/common/presentation/components/organisms/NotificationCenter/RenderNotification.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import { Card, createStyles, Fade, makeStyles, Theme, useTheme } from '@material-ui/core'; | ||
import { blue, green, grey, orange, red } from '@material-ui/core/colors'; | ||
import { Cancel } from '@material-ui/icons'; | ||
import { VariantType } from 'notistack'; | ||
import Optional from 'optional-js'; | ||
import * as React from 'react'; | ||
import { Snackbar } from 'src/common/presentation/state-module/snackbar'; | ||
|
||
const useStyles = makeStyles((theme: Theme) => createStyles({ | ||
card: { | ||
margin: theme.spacing(1.5), | ||
background: 'rgba(0, 0, 0, 0.35)', | ||
color: theme.palette.primary.contrastText, | ||
border: '0.5px solid #333', | ||
cursor: 'default', | ||
fontSize: '0.8rem' | ||
}, | ||
titleWrapper: { | ||
background: 'rgba(0, 0, 0, 0.20)', | ||
display: 'flex', | ||
justifyContent: 'space-between' | ||
}, | ||
title: { | ||
"& span:first-child": { | ||
marginLeft: 7, | ||
opacity: 0.9 | ||
}, | ||
"& span:not(:first-child)": { | ||
color: grey[400], | ||
margin: 3, | ||
lineHeight: 2 | ||
} | ||
}, | ||
notiContent: { | ||
margin: `${theme.spacing(1.3)}px ${theme.spacing(2)}px` | ||
}, | ||
message: { | ||
marginBottom: theme.spacing(1) | ||
}, | ||
messageOptions: { | ||
color: grey[400], | ||
whiteSpace: "pre" | ||
}, | ||
cancel: { | ||
color: grey[400], | ||
fontSize: '1rem', | ||
margin: '5px 8px 0px 5px', | ||
'&:active': { | ||
color: "#fff" | ||
} | ||
} | ||
})) | ||
|
||
interface Props { | ||
snackbar: Snackbar | ||
handleRemove(): any | ||
} | ||
|
||
const colorMap = new Map<VariantType, string>(); | ||
const colorWeight = 700; | ||
colorMap.set("default", "initial"); | ||
colorMap.set("error", red[colorWeight]); | ||
colorMap.set("info", blue[colorWeight]); | ||
colorMap.set("success", green[colorWeight]); | ||
colorMap.set("warning", orange[colorWeight]); | ||
|
||
const RenderNotification: React.FC<Props> = ({ snackbar, handleRemove }) => { | ||
const theme = useTheme(); | ||
const classes = useStyles(); | ||
|
||
const options = Optional.ofNullable(snackbar.options); | ||
|
||
const variant = options.map(o => o.variant).orElse("default"); | ||
const title = options.map(o => o.title).map(t => `: ${t}`).orElse(""); | ||
|
||
const [isCancelButtonHidden, setIsCancelButtonHidden] = React.useState(true); | ||
const showCancelButton = () => setIsCancelButtonHidden(false); | ||
const hideCancelButton = () => setIsCancelButtonHidden(true); | ||
|
||
const [checked, setChecked] = React.useState(true); | ||
const fadeOut = () => setChecked(false); | ||
|
||
const [ref] = React.useState(React.createRef<HTMLDivElement>()); | ||
const removeWithFadeoutAnimation = () => { | ||
if (!ref.current) { | ||
return; | ||
} | ||
|
||
const { leavingScreen } = theme.transitions.duration; | ||
|
||
const initialHeight = ref.current.offsetHeight; | ||
const deltaHeight = initialHeight / leavingScreen; | ||
|
||
const initialMargin = theme.spacing(1.5); | ||
const deltaMargin = initialMargin / leavingScreen; | ||
|
||
ref.current.style.height = initialHeight + "px"; | ||
ref.current.style.margin = initialMargin + "px"; | ||
|
||
// Remove element | ||
setTimeout(handleRemove, leavingScreen); | ||
fadeOut(); | ||
|
||
// Animation | ||
for (let i = 0; i < leavingScreen; i++) { | ||
setTimeout(() => { | ||
if (!ref.current) { | ||
return; | ||
} | ||
ref.current.style.height = (initialHeight - deltaHeight * i) + "px" | ||
ref.current.style.margin = (initialMargin - deltaMargin * i) + "px" | ||
}, i); | ||
} | ||
} | ||
|
||
return <> | ||
<Fade in={checked}> | ||
<Card | ||
ref={ref} | ||
className={classes.card} | ||
onMouseOver={showCancelButton} | ||
onMouseOut={hideCancelButton} | ||
> | ||
<div className={classes.titleWrapper}> | ||
<div className={classes.title}> | ||
<span style={{ color: colorMap.get(variant) }}> ● </span> | ||
<span>{variant}{title}</span> | ||
</div> | ||
<div hidden={isCancelButtonHidden} onMouseUp={removeWithFadeoutAnimation} > | ||
<Cancel className={classes.cancel} /> | ||
</div> | ||
</div> | ||
<div className={classes.notiContent}> | ||
<div className={classes.message}> | ||
{snackbar.message} | ||
</div> | ||
<div className={classes.messageOptions}> | ||
{Optional.ofNullable(snackbar.messageOptions).map((mo: any) => mo.e).orElse("")} | ||
</div> | ||
</div> | ||
</Card> | ||
</Fade> | ||
</>; | ||
} | ||
|
||
export default RenderNotification; |
1 change: 1 addition & 0 deletions
1
src/common/presentation/components/organisms/NotificationCenter/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './NotificationCenter' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
src/common/presentation/container/organisms/NotificationCenterContainer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import * as React from 'react'; | ||
import { connect } from 'react-redux'; | ||
import { bindActionCreators, Dispatch } from 'redux'; | ||
import NotificationCenter from '../../components/organisms/NotificationCenter'; | ||
import { RootState } from '../../state-module/root'; | ||
import * as snackbarModule from '../../state-module/snackbar'; | ||
import { Snackbar } from '../../state-module/snackbar'; | ||
|
||
interface Props { | ||
isNotificationCenterOpened: boolean | ||
snackbars: Snackbar[] | ||
dispatchers: typeof snackbarModule | ||
} | ||
|
||
const NotificationCenterContainer: React.FC<Props> = ({ | ||
isNotificationCenterOpened: opened, | ||
snackbars, | ||
dispatchers, | ||
}) => { | ||
const handleClose = dispatchers.closeNotificationCenter; | ||
const handleRemove = (key: string) => dispatchers.removeSnackbar({ key }); | ||
|
||
return <NotificationCenter | ||
snackbars={snackbars} | ||
opened={opened} | ||
handleClose={handleClose} | ||
handleRemove={handleRemove} | ||
/> | ||
} | ||
|
||
|
||
const mapStateToProps = ({ snackbar }: RootState) => ({ | ||
isNotificationCenterOpened: snackbar.isNotificationCenterOpened, | ||
snackbars: snackbar.snackbars | ||
}) | ||
|
||
const mapDispatchToProps = (dispatch: Dispatch<snackbarModule.Action>) => ({ | ||
dispatchers: bindActionCreators(snackbarModule, dispatch) | ||
}) | ||
|
||
export default connect(mapStateToProps, mapDispatchToProps)(NotificationCenterContainer); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.