Skip to content

Commit

Permalink
Lift defaults merging from UI to store
Browse files Browse the repository at this point in the history
- This makes all defaults work in headless mode aswell
- Removes complexity from UI
  • Loading branch information
timolins committed Dec 24, 2020
1 parent b6dc0d1 commit 8269ad7
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 36 deletions.
22 changes: 5 additions & 17 deletions src/components/toast-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import * as React from 'react';
import { useCallback } from 'react';
import { styled, keyframes, CSSAttribute } from 'goober';

import {
Toast,
ToastPosition,
ToastOptions,
resolveValueOrFunction,
} from '../core/types';
import { Toast, ToastPosition, resolveValueOrFunction } from '../core/types';
import { Indicator } from './indicator';
import { AnimatedIconWrapper } from './icon-wrapper';

Expand Down Expand Up @@ -50,7 +45,6 @@ interface ToastBarProps {
onHeight: (height: number) => void;

position: ToastPosition;
options: ToastOptions;
}

const getPositionStyle = (
Expand Down Expand Up @@ -104,26 +98,21 @@ const getAnimationStyle = (
};

export const ToastBar: React.FC<ToastBarProps> = React.memo(
({ toast, options, position, ...props }) => {
({ toast, position, ...props }) => {
const ref = useCallback((el: HTMLElement | null) => {
if (el) {
const boundingRect = el.getBoundingClientRect();
props.onHeight(boundingRect.height);
}
}, []);

const mergeOptions = {
...options,
...toast,
};

const positionStyle = getPositionStyle(position, props.offset);
const animationStyle = toast?.height
? getAnimationStyle(position, toast.visible)
: { opacity: 0 };

const renderIcon = () => {
const { icon, type, iconTheme } = mergeOptions;
const { icon, type, iconTheme } = toast;
if (icon !== undefined) {
if (typeof icon === 'string') {
return <AnimatedIconWrapper>{icon}</AnimatedIconWrapper>;
Expand All @@ -145,15 +134,14 @@ export const ToastBar: React.FC<ToastBarProps> = React.memo(
>
<ToastBarBase
ref={ref}
className={mergeOptions.className}
className={toast.className}
style={{
...animationStyle,
...options.style,
...toast.style,
}}
>
{renderIcon()}
<Message role={mergeOptions.role} aria-live={mergeOptions.ariaLive}>
<Message role={toast.role} aria-live={toast.ariaLive}>
{resolveValueOrFunction(toast.message, toast)}
</Message>
</ToastBarBase>
Expand Down
34 changes: 31 additions & 3 deletions src/core/store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { Toast } from './types';
import { DefaultToastOptions, Toast, ToastType } from './types';

const TOAST_LIMIT = 20;

Expand Down Expand Up @@ -126,7 +126,16 @@ export const dispatch = (action: Action) => {
});
};

export const useStore = () => {
const defaultTimeouts: {
[key in ToastType]: number;
} = {
blank: 4000,
error: 4000,
success: 2000,
loading: 30000,
};

export const useStore = (toastOptions: DefaultToastOptions = {}) => {
const [state, setState] = useState<State>(memoryState);
useEffect(() => {
listeners.push(setState);
Expand All @@ -138,5 +147,24 @@ export const useStore = () => {
};
}, [state]);

return state;
const mergedToasts = state.toasts.map((t) => ({
...toastOptions,
...toastOptions[t.type],
...t,
duration:
t.duration ||
toastOptions?.duration ||
toastOptions[t.type]?.duration ||
defaultTimeouts[t.type],
style: {
...toastOptions.style,
...toastOptions[t.type]?.style,
...t.style,
},
}));

return {
...state,
toasts: mergedToasts,
};
};
20 changes: 4 additions & 16 deletions src/core/use-toaster.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import { useEffect, useMemo } from 'react';
import { dispatch, ActionType, useStore } from './store';
import { DefaultToastOptions, ToastType } from './types';

const defaultTimeouts: Map<ToastType, number> = new Map<ToastType, number>([
['blank', 4000],
['error', 4000],
['success', 2000],
['loading', 30000],
]);
import { DefaultToastOptions } from './types';

export const useToaster = (toastOptions?: DefaultToastOptions) => {
const { toasts, pausedAt } = useStore();
const { toasts, pausedAt } = useStore(toastOptions);
const visibleToasts = toasts.filter((t) => t.visible);

useEffect(() => {
Expand All @@ -20,13 +13,8 @@ export const useToaster = (toastOptions?: DefaultToastOptions) => {

const now = Date.now();
const timeouts = toasts.map((t) => {
const duration =
t.duration ||
toastOptions?.[t.type]?.duration ||
toastOptions?.duration ||
defaultTimeouts.get(t.type) ||
4000;
const durationLeft = duration + t.pauseDuration - (now - t.createdAt);
const durationLeft =
(t.duration || 0) + t.pauseDuration - (now - t.createdAt);

const dismiss = () => {
dispatch({
Expand Down

0 comments on commit 8269ad7

Please sign in to comment.