UNPKG

zentrixui

Version:

ZentrixUI - A modern, highly customizable and accessible React file upload component library with multiple variants, JSON-based configuration, and excellent developer experience.

298 lines (297 loc) 11.3 kB
import { jsxs, jsx } from "react/jsx-runtime"; import { useMemo, useCallback } from "react"; import { formatErrorForUser } from "../../utils/error-handling.js"; import { cn } from "../../utils/theme.js"; import X from "../../node_modules/lucide-react/dist/esm/icons/x.js"; import CircleAlert from "../../node_modules/lucide-react/dist/esm/icons/circle-alert.js"; import Info from "../../node_modules/lucide-react/dist/esm/icons/info.js"; import TriangleAlert from "../../node_modules/lucide-react/dist/esm/icons/triangle-alert.js"; import CircleQuestionMark from "../../node_modules/lucide-react/dist/esm/icons/circle-question-mark.js"; import Trash2 from "../../node_modules/lucide-react/dist/esm/icons/trash-2.js"; import RefreshCw from "../../node_modules/lucide-react/dist/esm/icons/refresh-cw.js"; const ErrorIcon = ({ severity, className }) => { const iconProps = { className: cn("w-5 h-5 flex-shrink-0", className), "aria-hidden": true }; switch (severity) { case "critical": return /* @__PURE__ */ jsx(TriangleAlert, { ...iconProps, className: cn(iconProps.className, "text-red-600") }); case "high": return /* @__PURE__ */ jsx(CircleAlert, { ...iconProps, className: cn(iconProps.className, "text-red-500") }); case "medium": return /* @__PURE__ */ jsx(CircleAlert, { ...iconProps, className: cn(iconProps.className, "text-orange-500") }); case "low": return /* @__PURE__ */ jsx(Info, { ...iconProps, className: cn(iconProps.className, "text-blue-500") }); default: return /* @__PURE__ */ jsx(CircleAlert, { ...iconProps, className: cn(iconProps.className, "text-gray-500") }); } }; const ErrorActionButton = ({ action, error, onAction, compact }) => { const handleClick = useCallback(() => { if (action.handler) { action.handler(); } else { onAction(action, error); } }, [action, error, onAction]); const getActionIcon = () => { switch (action.type) { case "retry": return /* @__PURE__ */ jsx(RefreshCw, { className: "w-4 h-4" }); case "remove": case "clear": return /* @__PURE__ */ jsx(Trash2, { className: "w-4 h-4" }); case "contact": return /* @__PURE__ */ jsx(CircleQuestionMark, { className: "w-4 h-4" }); default: return null; } }; const getButtonClasses = () => { const baseClasses = [ "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-md", "focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors duration-200", action.disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer" ]; if (compact) { baseClasses.push("px-2 py-1 text-xs"); } if (action.primary) { baseClasses.push( "text-white bg-blue-600 hover:bg-blue-700 focus:ring-blue-500", action.disabled ? "" : "hover:bg-blue-700" ); } else { baseClasses.push( "text-gray-700 bg-gray-100 border border-gray-300 hover:bg-gray-200 focus:ring-gray-500", action.disabled ? "" : "hover:bg-gray-200" ); } return cn(...baseClasses); }; return /* @__PURE__ */ jsxs( "button", { type: "button", onClick: handleClick, disabled: action.disabled, className: getButtonClasses(), "aria-label": `${action.label} for ${error.context.fileName || "file"}`, children: [ getActionIcon(), action.label ] } ); }; const SingleErrorDisplay = ({ error, onAction, onDismiss, compact, showTechnicalDetails }) => { const getSeverityClasses = () => { switch (error.severity) { case "critical": return "border-red-300 bg-red-50 text-red-900"; case "high": return "border-red-200 bg-red-50 text-red-800"; case "medium": return "border-orange-200 bg-orange-50 text-orange-800"; case "low": return "border-blue-200 bg-blue-50 text-blue-800"; default: return "border-gray-200 bg-gray-50 text-gray-800"; } }; const containerClasses = cn( "border rounded-lg p-4 space-y-3", getSeverityClasses(), compact && "p-3 space-y-2" ); return /* @__PURE__ */ jsxs( "div", { className: containerClasses, role: "alert", "aria-live": "polite", "aria-labelledby": `error-title-${error.id}`, "aria-describedby": `error-description-${error.id}`, children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [ /* @__PURE__ */ jsx(ErrorIcon, { severity: error.severity }), /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [ /* @__PURE__ */ jsx( "h4", { id: `error-title-${error.id}`, className: cn( "font-medium", compact ? "text-sm" : "text-base" ), children: error.title } ), /* @__PURE__ */ jsx( "p", { id: `error-description-${error.id}`, className: cn( "mt-1 text-sm", compact ? "text-xs" : "text-sm" ), children: formatErrorForUser(error, true) } ), error.suggestions.length > 0 && !compact && /* @__PURE__ */ jsxs("div", { className: "mt-2", children: [ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium mb-1", children: "Suggestions:" }), /* @__PURE__ */ jsx("ul", { className: "text-sm space-y-1 list-disc list-inside", children: error.suggestions.map((suggestion, index) => /* @__PURE__ */ jsx("li", { children: suggestion }, index)) }) ] }), showTechnicalDetails && /* @__PURE__ */ jsxs("details", { className: "mt-3", children: [ /* @__PURE__ */ jsx("summary", { className: "text-sm font-medium cursor-pointer hover:underline", children: "Technical Details" }), /* @__PURE__ */ jsxs("div", { className: "mt-2 p-2 bg-gray-100 rounded text-xs font-mono overflow-auto", children: [ /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("strong", { children: "Error ID:" }), " ", error.id ] }), /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("strong", { children: "Code:" }), " ", error.code ] }), /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("strong", { children: "Type:" }), " ", error.type ] }), /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("strong", { children: "Message:" }), " ", error.technicalMessage ] }), error.context.timestamp && /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("strong", { children: "Time:" }), " ", error.context.timestamp.toISOString() ] }), error.context.fileName && /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("strong", { children: "File:" }), " ", error.context.fileName ] }), error.context.fileSize && /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx("strong", { children: "Size:" }), " ", error.context.fileSize, " bytes" ] }) ] }) ] }) ] }), onDismiss && /* @__PURE__ */ jsx( "button", { type: "button", onClick: () => onDismiss(error.id), className: "text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 rounded", "aria-label": "Dismiss error", children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" }) } ) ] }), error.actions.length > 0 && onAction && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 pt-2", children: error.actions.map((action) => /* @__PURE__ */ jsx( ErrorActionButton, { action, error, onAction, compact }, action.id )) }) ] } ); }; const ErrorDisplay = ({ errors, onAction, onDismiss, onDismissAll, className, compact = false, showTechnicalDetails = false, maxErrors = 5, groupByType = false }) => { const displayErrors = useMemo(() => { if (errors.length <= maxErrors) { return errors; } return errors.slice(0, maxErrors); }, [errors, maxErrors]); const groupedErrors = useMemo(() => { if (!groupByType) { return { ungrouped: displayErrors }; } return displayErrors.reduce((groups, error) => { const key = error.type; if (!groups[key]) { groups[key] = []; } groups[key].push(error); return groups; }, {}); }, [displayErrors, groupByType]); const hasMoreErrors = errors.length > maxErrors; if (errors.length === 0) { return null; } return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), role: "region", "aria-label": "Error messages", children: [ errors.length > 1 && onDismissAll && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ jsxs("h3", { className: "text-lg font-medium text-gray-900", children: [ errors.length, " Error", errors.length !== 1 ? "s" : "", " Occurred" ] }), /* @__PURE__ */ jsx( "button", { type: "button", onClick: onDismissAll, className: "text-sm text-gray-500 hover:text-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 rounded", children: "Dismiss All" } ) ] }), Object.entries(groupedErrors).map(([groupKey, groupErrors]) => /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [ groupByType && groupKey !== "ungrouped" && /* @__PURE__ */ jsxs("h4", { className: "text-md font-medium text-gray-800 capitalize", children: [ groupKey.replace("-", " "), " Errors (", groupErrors.length, ")" ] }), groupErrors.map((error) => /* @__PURE__ */ jsx( SingleErrorDisplay, { error, onAction, onDismiss, compact, showTechnicalDetails }, error.id )) ] }, groupKey)), hasMoreErrors && /* @__PURE__ */ jsx("div", { className: "text-center py-2", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500", children: [ "Showing ", maxErrors, " of ", errors.length, " errors" ] }) }) ] }); }; ErrorDisplay.displayName = "ErrorDisplay"; export { ErrorDisplay }; //# sourceMappingURL=error-display.js.map