Skip to content

Commit 7c4868b

Browse files
committed
feat: custom progress bar
1 parent 1e58b83 commit 7c4868b

File tree

7 files changed

+60
-44
lines changed

7 files changed

+60
-44
lines changed

src/components/ProgressBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export interface ProgressBarProps {
4141
className?: ToastClassName;
4242

4343
/**
44-
* Tell wether or not controlled progress bar is used
44+
* Tell whether a controlled progress bar is used
4545
*/
4646
controlledProgress?: boolean;
4747

src/components/Toast.cy.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ describe('Toast', () => {
248248
{
249249
type: 'react element',
250250
value: <div>hello</div>
251+
},
252+
{
253+
type: 'function',
254+
value: () => <div>hello</div>
251255
}
252256
]) {
253257
it(`render ${type}`, () => {

src/components/Toast.tsx

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import cx from 'clsx';
2-
import React, { cloneElement, isValidElement, ReactNode } from 'react';
2+
import React, { cloneElement, isValidElement } from 'react';
33

44
import { useToast } from '../hooks/useToast';
55
import { ToastProps } from '../types';
6-
import { Default, isFn } from '../utils';
6+
import { Default, isFn, renderContent } from '../utils';
77
import { CloseButton } from './CloseButton';
88
import { ProgressBar } from './ProgressBar';
99
import { getIcon } from './Icons';
@@ -99,22 +99,24 @@ export const Toast: React.FC<ToastProps> = props => {
9999
{icon}
100100
</div>
101101
)}
102-
{children as ReactNode}
102+
{renderContent(children, props, !isRunning)}
103103
{Close}
104-
<ProgressBar
105-
{...(updateId && !isProgressControlled ? { key: `pb-${updateId}` } : {})}
106-
rtl={rtl}
107-
theme={theme}
108-
delay={autoClose as number}
109-
isRunning={isRunning}
110-
isIn={isIn}
111-
closeToast={closeToast}
112-
hide={hideProgressBar}
113-
type={type}
114-
className={progressClassName}
115-
controlledProgress={isProgressControlled}
116-
progress={progress || 0}
117-
/>
104+
{!props.customProgressBar && (
105+
<ProgressBar
106+
{...(updateId && !isProgressControlled ? { key: `p-${updateId}` } : {})}
107+
rtl={rtl}
108+
theme={theme}
109+
delay={autoClose as number}
110+
isRunning={isRunning}
111+
isIn={isIn}
112+
closeToast={closeToast}
113+
hide={hideProgressBar}
114+
type={type}
115+
className={progressClassName}
116+
controlledProgress={isProgressControlled}
117+
progress={progress || 0}
118+
/>
119+
)}
118120
</div>
119121
</Transition>
120122
);

src/components/ToastContainer.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import cx from 'clsx';
22
import React, { useEffect, useRef, useState } from 'react';
33

44
import { toast } from '../core';
5-
import { useToastContainer } from '../hooks/useToastContainer';
5+
import { useToastContainer } from '../hooks';
66
import { useIsomorphicLayoutEffect } from '../hooks/useIsomorphicLayoutEffect';
77
import { ToastContainerProps, ToastPosition } from '../types';
88
import { Default, Direction, isFn, parseClassName } from '../utils';
@@ -137,7 +137,7 @@ export function ToastContainer(props: ToastContainerProps) {
137137
className={getClassName(position)}
138138
data-stacked={stacked}
139139
style={containerStyle}
140-
key={`container-${position}`}
140+
key={`c-${position}`}
141141
>
142142
{toastList.map(({ content, props: toastProps }) => {
143143
return (
@@ -146,7 +146,7 @@ export function ToastContainer(props: ToastContainerProps) {
146146
stacked={stacked}
147147
collapseAll={collapseAll}
148148
isIn={isToastActive(toastProps.toastId, toastProps.containerId)}
149-
key={`toast-${toastProps.key}`}
149+
key={`t-${toastProps.key}`}
150150
>
151151
{content}
152152
</Toast>

src/core/containerObserver.ts

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { cloneElement, isValidElement, ReactElement } from 'react';
21
import {
32
Id,
43
NotValidatedToastProps,
54
OnChangeCallback,
65
Toast,
76
ToastContainerProps,
87
ToastContent,
9-
ToastContentProps,
108
ToastProps
119
} from '../types';
12-
import { canBeRendered, getAutoCloseDelay, isFn, isNum, isStr, parseClassName, toToastItem } from '../utils';
10+
import { canBeRendered, getAutoCloseDelay, isNum, parseClassName, toToastItem } from '../utils';
1311

1412
type Notify = () => void;
1513

@@ -89,10 +87,6 @@ export function createContainerObserver(
8987
if (shouldIgnoreToast(options)) return;
9088

9189
const { toastId, updateId, data, staleId, delay } = options;
92-
const closeToast = (removedByUser?: true) => {
93-
toasts.get(toastId)!.removalReason = removedByUser;
94-
removeToast(toastId);
95-
};
9690

9791
const isNotAnUpdate = updateId == null;
9892

@@ -106,11 +100,14 @@ export function createContainerObserver(
106100
toastId,
107101
updateId,
108102
data,
109-
closeToast,
110103
isIn: false,
111104
className: parseClassName(options.className || props.toastClassName),
112105
progressClassName: parseClassName(options.progressClassName || props.progressClassName),
113106
autoClose: options.isLoading ? false : getAutoCloseDelay(options.autoClose, props.autoClose),
107+
closeToast(reason?: true) {
108+
toasts.get(toastId)!.removalReason = reason;
109+
removeToast(toastId);
110+
},
114111
deleteToast() {
115112
const toastToRemove = toasts.get(toastId);
116113

@@ -139,20 +136,8 @@ export function createContainerObserver(
139136
toastProps.closeButton = canBeRendered(props.closeButton) ? props.closeButton : true;
140137
}
141138

142-
let toastContent = content;
143-
144-
if (isValidElement(content) && !isStr(content.type)) {
145-
toastContent = cloneElement<ToastContentProps>(content as ReactElement<any>, {
146-
closeToast,
147-
toastProps,
148-
data
149-
});
150-
} else if (isFn(content)) {
151-
toastContent = content({ closeToast, toastProps, data: data as TData });
152-
}
153-
154139
const activeToast = {
155-
content: toastContent,
140+
content,
156141
props: toastProps,
157142
staleId
158143
} as Toast;

src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type CloseToastFunc = (reason?: boolean | string) => void;
1717
export interface ToastContentProps<Data = unknown> {
1818
closeToast: CloseToastFunc;
1919
toastProps: ToastProps;
20+
isPaused: boolean;
2021
data: Data;
2122
}
2223

@@ -150,6 +151,8 @@ interface CommonOptions {
150151
* `Default: 'light'`
151152
*/
152153
theme?: Theme;
154+
155+
customProgressBar?: boolean;
153156
}
154157

155158
export interface ToastOptions<Data = unknown> extends CommonOptions {

src/utils/mapper.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { Toast, ToastItem, ToastItemStatus } from '../types';
1+
import { Toast, ToastContentProps, ToastItem, ToastItemStatus, ToastProps } from '../types';
2+
import { cloneElement, isValidElement, ReactElement } from 'react';
3+
import { isFn, isStr } from './propValidator';
24

35
export function toToastItem(toast: Toast, status: ToastItemStatus): ToastItem {
46
return {
5-
content: toast.content,
7+
content: renderContent(toast.content, toast.props),
68
containerId: toast.props.containerId,
79
id: toast.props.toastId,
810
theme: toast.props.theme,
@@ -14,3 +16,23 @@ export function toToastItem(toast: Toast, status: ToastItemStatus): ToastItem {
1416
status
1517
};
1618
}
19+
20+
export function renderContent(content: unknown, props: ToastProps, isPaused: boolean = false) {
21+
if (isValidElement(content) && !isStr(content.type)) {
22+
return cloneElement<ToastContentProps>(content as ReactElement<any>, {
23+
closeToast: props.closeToast,
24+
toastProps: props,
25+
data: props.data,
26+
isPaused
27+
});
28+
} else if (isFn(content)) {
29+
return content({
30+
closeToast: props.closeToast,
31+
toastProps: props,
32+
data: props.data,
33+
isPaused
34+
});
35+
}
36+
37+
return content;
38+
}

0 commit comments

Comments
 (0)