-
Notifications
You must be signed in to change notification settings - Fork 750
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: Toast * perf(toast): Clear timeout before creating * build: Update packages * chore: Fix warning in story * fix(text): Add semicolon after textTransform to prevent invalid 'space' styles Co-authored-by: RichardPK <r.phillips.kerr@gmail.com>
- Loading branch information
1 parent
8d2cb19
commit ed5faeb
Showing
9 changed files
with
1,216 additions
and
1,004 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
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
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,64 @@ | ||
import React, { useCallback, useEffect, useRef } from "react"; | ||
import { CSSTransition } from "react-transition-group"; | ||
import styled from "styled-components"; | ||
import { Alert } from "../../components/Alert"; | ||
import { ToastProps } from "./types"; | ||
|
||
const StyledToast = styled.div` | ||
right: 16px; | ||
position: fixed; | ||
max-width: calc(100% - 32px); | ||
transition: all 250ms ease-in; | ||
width: 100%; | ||
${({ theme }) => theme.mediaQueries.sm} { | ||
max-width: 400px; | ||
} | ||
`; | ||
|
||
const Toast: React.FC<ToastProps> = ({ alert, onRemove, style, ttl, ...props }) => { | ||
const timer = useRef<number>(); | ||
const ref = useRef(null); | ||
const removeHandler = useRef(onRemove); | ||
const { id, title, description, type } = alert; | ||
|
||
const handleRemove = useCallback(() => removeHandler.current(id), [id, removeHandler]); | ||
|
||
const handleMouseEnter = () => { | ||
clearTimeout(timer.current); | ||
}; | ||
|
||
const handleMouseLeave = () => { | ||
if (timer.current) { | ||
clearTimeout(timer.current); | ||
} | ||
|
||
timer.current = window.setTimeout(() => { | ||
handleRemove(); | ||
}, ttl); | ||
}; | ||
|
||
useEffect(() => { | ||
if (timer.current) { | ||
clearTimeout(timer.current); | ||
} | ||
|
||
timer.current = window.setTimeout(() => { | ||
handleRemove(); | ||
}, ttl); | ||
|
||
return () => { | ||
clearTimeout(timer.current); | ||
}; | ||
}, [timer, ttl, handleRemove]); | ||
|
||
return ( | ||
<CSSTransition nodeRef={ref} timeout={250} style={style} {...props}> | ||
<StyledToast ref={ref} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}> | ||
<Alert title={title} description={description} variant={type} onClick={handleRemove} /> | ||
</StyledToast> | ||
</CSSTransition> | ||
); | ||
}; | ||
|
||
export default Toast; |
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,49 @@ | ||
import React from "react"; | ||
import { TransitionGroup } from "react-transition-group"; | ||
import styled from "styled-components"; | ||
import Toast from "./Toast"; | ||
import { ToastContainerProps } from "./types"; | ||
|
||
const ZINDEX = 1000; | ||
const TOP_POSITION = 80; // Initial position from the top | ||
|
||
const StyledToastContainer = styled.div` | ||
.enter, | ||
.appear { | ||
opacity: 0.01; | ||
} | ||
.enter.enter-active, | ||
.appear.appear-active { | ||
opacity: 1; | ||
transition: opacity 250ms ease-in; | ||
} | ||
.exit { | ||
opacity: 1; | ||
} | ||
.exit.exit-active { | ||
opacity: 0.01; | ||
transition: opacity 250ms ease-out; | ||
} | ||
`; | ||
|
||
const ToastContainer: React.FC<ToastContainerProps> = ({ alerts, onRemove, ttl = 6000, stackSpacing = 24 }) => { | ||
return ( | ||
<StyledToastContainer> | ||
<TransitionGroup> | ||
{alerts.map((alert, index) => { | ||
const zIndex = (ZINDEX - index).toString(); | ||
const top = TOP_POSITION + index * stackSpacing; | ||
|
||
return ( | ||
<Toast key={alert.id} alert={alert} onRemove={onRemove} ttl={ttl} style={{ top: `${top}px`, zIndex }} /> | ||
); | ||
})} | ||
</TransitionGroup> | ||
</StyledToastContainer> | ||
); | ||
}; | ||
|
||
export default ToastContainer; |
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,48 @@ | ||
import React, { useState } from "react"; | ||
import { sample } from "lodash"; | ||
import { alertVariants } from "../../components/Alert"; | ||
import Button from "../../components/Button/Button"; | ||
import ToastContainer from "./ToastContainer"; | ||
|
||
export default { | ||
title: "Widgets/Toast", | ||
component: ToastContainer, | ||
argTypes: {}, | ||
}; | ||
|
||
export const Default: React.FC = () => { | ||
const [alerts, setAlerts] = useState([]); | ||
|
||
const handleClick = (description = "") => { | ||
const now = Date.now(); | ||
const randomAlert = { | ||
id: `id-${now}`, | ||
title: `Title: ${now}`, | ||
description, | ||
type: alertVariants[sample(Object.keys(alertVariants))], | ||
}; | ||
|
||
setAlerts((prevAlerts) => [randomAlert, ...prevAlerts]); | ||
}; | ||
|
||
const handleRemove = (id: string) => { | ||
setAlerts((prevAlerts) => prevAlerts.filter((prevAlert) => prevAlert.id !== id)); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<Button type="button" variant="secondary" onClick={() => handleClick()}> | ||
Random Toast | ||
</Button> | ||
<Button | ||
type="button" | ||
variant="secondary" | ||
ml="8px" | ||
onClick={() => handleClick("This is a description to explain more about the alert")} | ||
> | ||
Random Toast with Description | ||
</Button> | ||
<ToastContainer alerts={alerts} onRemove={handleRemove} /> | ||
</div> | ||
); | ||
}; |
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,2 @@ | ||
export { default as ToastContainer } from "./ToastContainer"; | ||
export type { ToastContainerProps } from "./types"; |
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,27 @@ | ||
export enum AlertType { | ||
SUCCESS = "success", | ||
DANGER = "danger", | ||
WARNING = "warning", | ||
INFO = "info", | ||
} | ||
|
||
export interface Alert { | ||
id: string; | ||
type: AlertType; | ||
title: string; | ||
description?: string; | ||
} | ||
|
||
export interface ToastContainerProps { | ||
alerts: Alert[]; | ||
stackSpacing?: number; | ||
ttl?: number; | ||
onRemove: (id: string) => void; | ||
} | ||
|
||
export interface ToastProps { | ||
alert: Alert; | ||
onRemove: ToastContainerProps["onRemove"]; | ||
ttl: number; | ||
style: Partial<CSSStyleDeclaration>; | ||
} |
Oops, something went wrong.