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
JavaScript
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