UNPKG

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
'use strict'; 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