diff --git a/src/ErrorBoundary/BasicErrorBoundary.tsx b/src/ErrorBoundary/BasicErrorBoundary.tsx
new file mode 100644
index 0000000..80e76f1
--- /dev/null
+++ b/src/ErrorBoundary/BasicErrorBoundary.tsx
@@ -0,0 +1,70 @@
+import React, { type PropsWithChildren } from 'react';
+
+interface ErrorBoundaryProps extends PropsWithChildren {
+ namespace?: string;
+ onReportClick?: () => void;
+ onResetClick?: () => void;
+ onRetryClick?: () => void;
+ reportAPI?: string;
+}
+
+interface ErrorBoundaryState {
+ error: any;
+ errorInfo: any;
+ reported?: boolean;
+ resetted?: boolean;
+ showDetail: boolean;
+ timer?: NodeJS.Timeout | undefined;
+ try: boolean;
+ tryCnt: number;
+}
+
+export class ReactBasicErrorBoundary extends React.PureComponent<
+ ErrorBoundaryProps,
+ ErrorBoundaryState
+> {
+ constructor(props: ErrorBoundaryProps) {
+ super(props);
+
+ this.state = {
+ error: null,
+ errorInfo: null,
+ showDetail: false,
+ timer: undefined,
+ try: false,
+ tryCnt: 0,
+ };
+ }
+
+ componentDidCatch(error: any, errorInfo: any) {
+ // Catch errors in any components below and re-render with error message
+ this.setState({
+ error,
+ errorInfo,
+ try: false,
+ });
+ // You can also log error messages to an error reporting service here
+ }
+
+ render() {
+ if (this.state.errorInfo) {
+ // Error path
+ return (
+
+
Error
+ {this.state.error && (
+ <>
+ In: {this.props.namespace ?? 'default'}
+ {this.state.error.toString()}
+ >
+ )}
+
+ );
+ }
+
+ // Normally, just render children
+ return this.props.children;
+ }
+}
+
+export default ReactBasicErrorBoundary;
diff --git a/src/ErrorBoundary/ErrorBoundary.tsx b/src/ErrorBoundary/ErrorBoundary.tsx
new file mode 100644
index 0000000..0ee6251
--- /dev/null
+++ b/src/ErrorBoundary/ErrorBoundary.tsx
@@ -0,0 +1,240 @@
+import { Button, Code, Collapse, Group, Paper, rem, Text } from '@mantine/core';
+import { IconExclamationCircle } from '@tabler/icons-react';
+import React, { type PropsWithChildren } from 'react';
+
+let ErrorBoundaryOptions = {
+ disabled: false,
+ onError: undefined,
+} as {
+ disabled?: boolean;
+ onError?: (error: any, errorInfo: any) => void;
+};
+
+export const SetErrorBoundaryOptions = (options: typeof ErrorBoundaryOptions) => {
+ ErrorBoundaryOptions = { ...ErrorBoundaryOptions, ...options };
+};
+
+export const GetErrorBoundaryOptions = () => {
+ return { ...ErrorBoundaryOptions };
+};
+
+interface ErrorBoundaryProps extends PropsWithChildren {
+ namespace?: string;
+ onReportClick?: () => void;
+ onResetClick?: () => void;
+ onRetryClick?: () => void;
+ reportAPI?: string;
+}
+
+interface ErrorBoundaryState {
+ error: any;
+ errorInfo: any;
+ reported?: boolean;
+ showDetail: boolean;
+ timer?: NodeJS.Timeout | undefined;
+ try: boolean;
+ tryCnt: number;
+ wasReset?: boolean;
+}
+
+export class ReactErrorBoundary extends React.Component {
+ constructor(props: ErrorBoundaryProps) {
+ super(props);
+
+ this.state = {
+ error: null,
+ errorInfo: null,
+ showDetail: false,
+ timer: undefined,
+ try: false,
+ tryCnt: 0,
+ };
+ }
+
+ componentDidCatch(error: any, errorInfo: any) {
+ if (GetErrorBoundaryOptions()?.disabled) {
+ throw Error('ErrorBoundary pass through enabled, rethrowing error', {
+ cause: error,
+ //@ts-ignore
+ info: errorInfo,
+ });
+ }
+ // Catch errors in any components below and re-render with error message
+ this.setState({
+ error,
+ errorInfo,
+ try: false,
+ });
+ if (typeof GetErrorBoundaryOptions()?.onError === 'function') {
+ GetErrorBoundaryOptions()?.onError?.(error, errorInfo);
+ }
+ // You can also log error messages to an error reporting service here
+ }
+
+ render() {
+ if (this.state.errorInfo) {
+ // Error path
+ return (
+
+
+
+ A react error has occurred!
+
+
You can try one of these solutions to get back on track:
+
+ - Report the error to our team
+ - Hit the keys Ctrl-Shift-R
+ - Make sure your web browser is up to date
+ - Clear your browser cache and cookies
+ - Try using a different web browser
+ - Reset your layout and settings
+ - Retry loading the application
+
+
+
+
+
+
+
+
+
+ {this.state.error && (
+
+ {this.props.namespace && In: {this.props.namespace}}
+ {this.state.error.toString()}
+
+ )}
+
+
+
+ {this.state.errorInfo.componentStack}
+
+
+
+ );
+ }
+
+ //Retry code, if you do it too many times, it will stop
+ if (this.state.try) {
+ if (!this.state.timer) {
+ const tm = setTimeout(() => {
+ clearTimeout(this.state.timer);
+ this.setState((state) => ({
+ ...state,
+ timer: undefined,
+ try: false,
+ }));
+ }, 1000);
+
+ this.setState((state) => ({
+ ...state,
+ timer: tm,
+ }));
+ }
+ return Retrying {this.state.tryCnt}...
;
+ }
+
+ // Normally, just render children
+ return this.props.children;
+ }
+
+ async report() {
+ if (this.props?.onReportClick) {
+ this.props.onReportClick();
+ this.setState(() => ({
+ reported: true,
+ }));
+ return;
+ }
+ }
+
+ reset() {
+ if (this.props?.onResetClick) {
+ this.props.onResetClick();
+ this.setState(() => ({
+ wasReset: true,
+ }));
+ return;
+ }
+
+ this.setState(() => ({
+ wasReset: true,
+ }));
+
+ window.location.reload();
+ }
+
+ retry() {
+ this.setState({
+ try: true,
+ tryCnt: this.state.tryCnt + 1,
+ });
+ if (this.props?.onRetryClick) {
+ this.props.onRetryClick();
+ return;
+ }
+ this.setState({
+ error: undefined,
+ errorInfo: undefined,
+ });
+ this.forceUpdate();
+ }
+}
+
+export default ReactErrorBoundary;
diff --git a/src/ErrorBoundary/index.ts b/src/ErrorBoundary/index.ts
new file mode 100644
index 0000000..9d56e81
--- /dev/null
+++ b/src/ErrorBoundary/index.ts
@@ -0,0 +1,2 @@
+export { default as ReactBasicErrorBoundary } from './BasicErrorBoundary';
+export { default as ReactErrorBoundary } from './ErrorBoundary';