react-amiga-guru-meditation
Version:
A React Error Boundary styled like the Amiga Guru Meditation — accessible, themeable, zero runtime dependencies, ARIA-compliant, CSS variables theming.
171 lines (167 loc) • 5.45 kB
JavaScript
;
var react = require('react');
var reactDom = require('react-dom');
var jsxRuntime = require('react/jsx-runtime');
// src/AmigaErrorBoundary.tsx
// src/AmigaErrorBoundary.module.css
var AmigaErrorBoundary_default = {
root: "AmigaErrorBoundary_root",
noBlink: "AmigaErrorBoundary_noBlink",
portal: "AmigaErrorBoundary_portal",
title: "AmigaErrorBoundary_title",
message: "AmigaErrorBoundary_message",
code: "AmigaErrorBoundary_code",
details: "AmigaErrorBoundary_details"
};
var DEFAULT_TITLE = "Software Failure. Press left mouse button to continue.";
function shallowDiff(a, b) {
if (a === b) return false;
if (!a || !b) return true;
if (a.length !== b.length) return true;
for (let i = 0; i < a.length; i += 1) {
if (!Object.is(a[i], b[i])) return true;
}
return false;
}
var AmigaErrorBoundary = class extends react.Component {
constructor() {
super(...arguments);
this.state = { error: null, errorInfo: null };
this.resetErrorBoundary = () => {
this.resetWithReason("imperative");
};
}
static getDerivedStateFromError(error) {
return { error };
}
componentDidCatch(error, errorInfo) {
this.setState({ errorInfo });
this.props.onError?.(error, errorInfo);
}
componentDidUpdate(prevProps) {
if (this.state.error !== null && shallowDiff(prevProps.resetKeys, this.props.resetKeys)) {
this.resetWithReason("keys");
}
}
resetWithReason(reason) {
if (this.state.error === null && this.state.errorInfo === null) return;
this.setState({ error: null, errorInfo: null });
this.props.onReset?.({ reason });
}
render() {
const { error: caught, errorInfo } = this.state;
const {
show = false,
error: forcedError,
fallback,
fallbackRender,
children,
portal,
title,
formatGuru,
showDetails,
noBlink,
className,
style
} = this.props;
const errorToShow = caught ?? (show ? forcedError ?? new Error("Software Failure") : null);
if (!errorToShow) {
return children ?? null;
}
let node;
if (fallbackRender) {
node = fallbackRender({
error: errorToShow,
errorInfo,
resetErrorBoundary: this.resetErrorBoundary
});
} else if (fallback !== void 0) {
node = fallback;
} else {
node = /* @__PURE__ */ jsxRuntime.jsx(
DefaultGuru,
{
error: errorToShow,
errorInfo,
title,
formatGuru,
showDetails: showDetails ?? true,
noBlink: noBlink ?? false,
className,
style,
portaled: Boolean(portal)
}
);
}
if (portal) {
return /* @__PURE__ */ jsxRuntime.jsx(PortalWrapper, { target: portal, children: node });
}
return node;
}
};
AmigaErrorBoundary.displayName = "AmigaErrorBoundary";
function DefaultGuru({
error,
errorInfo,
title,
formatGuru,
showDetails,
noBlink,
className,
style,
portaled
}) {
const message = formatGuru ? formatGuru(error) : error.message || error.toString();
const classes = [AmigaErrorBoundary_default.root, noBlink && !portaled ? AmigaErrorBoundary_default.noBlink : null, className].filter(Boolean).join(" ");
return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", "aria-live": "assertive", className: classes, style, children: [
/* @__PURE__ */ jsxRuntime.jsx("p", { className: AmigaErrorBoundary_default.title, children: title ?? DEFAULT_TITLE }),
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: AmigaErrorBoundary_default.message, children: [
"Guru Meditation: ",
/* @__PURE__ */ jsxRuntime.jsx("span", { className: AmigaErrorBoundary_default.code, children: message })
] }),
showDetails && errorInfo?.componentStack ? /* @__PURE__ */ jsxRuntime.jsxs("details", { className: AmigaErrorBoundary_default.details, children: [
/* @__PURE__ */ jsxRuntime.jsx("summary", { children: "Component stack" }),
/* @__PURE__ */ jsxRuntime.jsx("pre", { children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: errorInfo.componentStack }) })
] }) : null
] });
}
function PortalWrapper({ target, children }) {
const [container, setContainer] = react.useState(null);
react.useEffect(() => {
if (typeof document === "undefined") return;
if (target instanceof HTMLElement) {
setContainer(target);
return;
}
const id = typeof target === "string" ? target : "amiga-guru";
let el = document.getElementById(id);
let created = false;
if (!el) {
el = document.createElement("div");
el.id = id;
document.body.appendChild(el);
created = true;
}
setContainer(el);
return () => {
if (created && el?.parentNode) {
el.parentNode.removeChild(el);
}
};
}, [target]);
if (!container) return null;
return reactDom.createPortal(/* @__PURE__ */ jsxRuntime.jsx("div", { className: AmigaErrorBoundary_default.portal, children }), container);
}
function useAmigaGuru() {
const [, setState] = react.useState();
return react.useCallback((error) => {
setState(() => {
throw error instanceof Error ? error : new Error(String(error));
});
}, []);
}
exports.AmigaErrorBoundary = AmigaErrorBoundary;
exports.GuruMeditation = AmigaErrorBoundary;
exports.useAmigaGuru = useAmigaGuru;
//# sourceMappingURL=index.cjs.map
//# sourceMappingURL=index.cjs.map