react-pdf-flipbook-viewer
Version:
A customizable React component to render PDF documents in a flipbook-style viewer — perfect for brochures, magazines, and interactive documents. ## Features
131 lines (130 loc) • 3.83 kB
JavaScript
import { useEffect, useState } from "react";
// Inspired by react-hot-toast library
const TOAST_LIMIT = 1;
const TOAST_REMOVE_DELAY = 1000000;
var actionTypes;
(function (actionTypes) {
actionTypes["ADD_TOAST"] = "ADD_TOAST";
actionTypes["UPDATE_TOAST"] = "UPDATE_TOAST";
actionTypes["DISMISS_TOAST"] = "DISMISS_TOAST";
actionTypes["REMOVE_TOAST"] = "REMOVE_TOAST";
})(actionTypes || (actionTypes = {}));
let count = 0;
function genId() {
count = (count + 1) % Number.MAX_SAFE_INTEGER;
return count.toString();
}
const toastTimeouts = new Map();
const addToRemoveQueue = (toastId) => {
if (toastTimeouts.has(toastId)) {
return;
}
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId);
dispatch({
type: actionTypes.REMOVE_TOAST,
toastId: toastId,
});
}, TOAST_REMOVE_DELAY);
toastTimeouts.set(toastId, timeout);
};
export const reducer = (state, action) => {
switch (action.type) {
case actionTypes.ADD_TOAST:
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
};
case actionTypes.UPDATE_TOAST:
return {
...state,
toasts: state.toasts.map((t) => t.id === action.toast?.id ? { ...t, ...action.toast } : t),
};
case actionTypes.DISMISS_TOAST: {
const { toastId } = action;
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId);
}
else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id);
});
}
return {
...state,
toasts: state.toasts.map((t) => t.id === toastId || toastId === undefined
? {
...t,
open: false,
}
: t),
};
}
case actionTypes.REMOVE_TOAST:
if (action.toastId === undefined) {
return {
...state,
toasts: [],
};
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
};
default:
return state;
}
};
const listeners = [];
let memoryState = { toasts: [] };
function dispatch(action) {
memoryState = reducer(memoryState, action);
listeners.forEach((listener) => {
listener(memoryState);
});
}
function toast({ ...props }) {
const id = genId();
const update = (props) => dispatch({
type: actionTypes.UPDATE_TOAST,
toast: { ...props, id },
});
const dismiss = () => dispatch({ type: actionTypes.DISMISS_TOAST, toastId: id });
dispatch({
type: actionTypes.ADD_TOAST,
toast: {
...props,
id,
open: true,
onOpenChange: (open) => {
if (!open)
dismiss();
},
},
});
return {
id,
dismiss,
update,
};
}
function useToast() {
const [state, setState] = useState(memoryState);
useEffect(() => {
listeners.push(setState);
return () => {
const index = listeners.indexOf(setState);
if (index > -1) {
listeners.splice(index, 1);
}
};
}, [state]);
return {
...state,
toast,
dismiss: (toastId) => dispatch({ type: actionTypes.DISMISS_TOAST, toastId }),
};
}
export { useToast, toast };