@diwauhris/ui 1.7.21 → 1.7.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.cjs CHANGED
@@ -2100,12 +2100,42 @@ var STYLES = {
2100
2100
  warning: "border-amber-200 bg-amber-50 text-amber-800",
2101
2101
  info: "border-sky-200 bg-sky-50 text-sky-800"
2102
2102
  };
2103
- function ErrorBanner({ message, tone = "error", className = "" }) {
2104
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2103
+ function ErrorBanner({ message, title, tone = "error", className = "", icon, action, onDismiss, dismissLabel = "Dismiss" }) {
2104
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2105
2105
  role: tone === "error" ? "alert" : void 0,
2106
2106
  "aria-live": tone !== "error" ? "polite" : void 0,
2107
- className: cn("rounded-2xl border px-5 py-4 text-sm font-bold", STYLES[tone], className),
2108
- children: message
2107
+ className: cn("flex items-start gap-3 rounded-2xl border px-5 py-4 text-sm font-bold", STYLES[tone], className),
2108
+ children: [
2109
+ icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2110
+ className: "mt-0.5 shrink-0",
2111
+ "aria-hidden": "true",
2112
+ children: icon
2113
+ }),
2114
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2115
+ className: "min-w-0 flex-1",
2116
+ children: [title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2117
+ className: "mb-0.5",
2118
+ children: title
2119
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2120
+ className: cn(title && "font-medium"),
2121
+ children: message
2122
+ })]
2123
+ }),
2124
+ action && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2125
+ className: "shrink-0",
2126
+ children: action
2127
+ }),
2128
+ onDismiss && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2129
+ type: "button",
2130
+ onClick: onDismiss,
2131
+ className: "shrink-0 rounded-lg p-1 transition-colors hover:bg-black/5",
2132
+ "aria-label": dismissLabel,
2133
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, {
2134
+ size: 16,
2135
+ "aria-hidden": "true"
2136
+ })
2137
+ })
2138
+ ]
2109
2139
  });
2110
2140
  }
2111
2141
  //#endregion
@@ -3976,16 +4006,21 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
3976
4006
  const panelHeight = panelRef.current?.offsetHeight ?? items.length * ROW_HEIGHT + HEADER_HEIGHT;
3977
4007
  const spaceBelow = window.innerHeight - rect.bottom;
3978
4008
  const flipUp = spaceBelow < panelHeight + GAP + MARGIN && rect.top > spaceBelow;
3979
- const panelWidth = Math.max(panelRef.current?.offsetWidth ?? 0, rect.width, PANEL_MIN_WIDTH);
3980
- const desiredLeft = align === "left" ? rect.left : rect.right - panelWidth;
3981
- const maxLeft = window.innerWidth - panelWidth - MARGIN;
3982
- const left = Math.min(Math.max(MARGIN, desiredLeft), Math.max(MARGIN, maxLeft));
4009
+ const available = window.innerWidth - 16;
4010
+ const horizontal = align === "left" ? {
4011
+ left: Math.max(MARGIN, Math.min(rect.left, window.innerWidth - MARGIN)),
4012
+ right: "auto"
4013
+ } : {
4014
+ right: Math.max(MARGIN, window.innerWidth - rect.right),
4015
+ left: "auto"
4016
+ };
3983
4017
  setPanelStyle({
3984
4018
  position: "fixed",
3985
4019
  ...flipUp ? { bottom: Math.max(MARGIN, window.innerHeight - rect.top + GAP) } : { top: rect.bottom + GAP },
3986
- left,
4020
+ ...horizontal,
3987
4021
  zIndex: 9999,
3988
- minWidth: Math.max(rect.width, PANEL_MIN_WIDTH)
4022
+ minWidth: PANEL_MIN_WIDTH,
4023
+ maxWidth: available
3989
4024
  });
3990
4025
  };
3991
4026
  reposition();
package/lib/index.mjs CHANGED
@@ -2076,12 +2076,42 @@ var STYLES = {
2076
2076
  warning: "border-amber-200 bg-amber-50 text-amber-800",
2077
2077
  info: "border-sky-200 bg-sky-50 text-sky-800"
2078
2078
  };
2079
- function ErrorBanner({ message, tone = "error", className = "" }) {
2080
- return /* @__PURE__ */ jsx("div", {
2079
+ function ErrorBanner({ message, title, tone = "error", className = "", icon, action, onDismiss, dismissLabel = "Dismiss" }) {
2080
+ return /* @__PURE__ */ jsxs("div", {
2081
2081
  role: tone === "error" ? "alert" : void 0,
2082
2082
  "aria-live": tone !== "error" ? "polite" : void 0,
2083
- className: cn("rounded-2xl border px-5 py-4 text-sm font-bold", STYLES[tone], className),
2084
- children: message
2083
+ className: cn("flex items-start gap-3 rounded-2xl border px-5 py-4 text-sm font-bold", STYLES[tone], className),
2084
+ children: [
2085
+ icon && /* @__PURE__ */ jsx("span", {
2086
+ className: "mt-0.5 shrink-0",
2087
+ "aria-hidden": "true",
2088
+ children: icon
2089
+ }),
2090
+ /* @__PURE__ */ jsxs("div", {
2091
+ className: "min-w-0 flex-1",
2092
+ children: [title && /* @__PURE__ */ jsx("div", {
2093
+ className: "mb-0.5",
2094
+ children: title
2095
+ }), /* @__PURE__ */ jsx("span", {
2096
+ className: cn(title && "font-medium"),
2097
+ children: message
2098
+ })]
2099
+ }),
2100
+ action && /* @__PURE__ */ jsx("div", {
2101
+ className: "shrink-0",
2102
+ children: action
2103
+ }),
2104
+ onDismiss && /* @__PURE__ */ jsx("button", {
2105
+ type: "button",
2106
+ onClick: onDismiss,
2107
+ className: "shrink-0 rounded-lg p-1 transition-colors hover:bg-black/5",
2108
+ "aria-label": dismissLabel,
2109
+ children: /* @__PURE__ */ jsx(X, {
2110
+ size: 16,
2111
+ "aria-hidden": "true"
2112
+ })
2113
+ })
2114
+ ]
2085
2115
  });
2086
2116
  }
2087
2117
  //#endregion
@@ -3952,16 +3982,21 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
3952
3982
  const panelHeight = panelRef.current?.offsetHeight ?? items.length * ROW_HEIGHT + HEADER_HEIGHT;
3953
3983
  const spaceBelow = window.innerHeight - rect.bottom;
3954
3984
  const flipUp = spaceBelow < panelHeight + GAP + MARGIN && rect.top > spaceBelow;
3955
- const panelWidth = Math.max(panelRef.current?.offsetWidth ?? 0, rect.width, PANEL_MIN_WIDTH);
3956
- const desiredLeft = align === "left" ? rect.left : rect.right - panelWidth;
3957
- const maxLeft = window.innerWidth - panelWidth - MARGIN;
3958
- const left = Math.min(Math.max(MARGIN, desiredLeft), Math.max(MARGIN, maxLeft));
3985
+ const available = window.innerWidth - 16;
3986
+ const horizontal = align === "left" ? {
3987
+ left: Math.max(MARGIN, Math.min(rect.left, window.innerWidth - MARGIN)),
3988
+ right: "auto"
3989
+ } : {
3990
+ right: Math.max(MARGIN, window.innerWidth - rect.right),
3991
+ left: "auto"
3992
+ };
3959
3993
  setPanelStyle({
3960
3994
  position: "fixed",
3961
3995
  ...flipUp ? { bottom: Math.max(MARGIN, window.innerHeight - rect.top + GAP) } : { top: rect.bottom + GAP },
3962
- left,
3996
+ ...horizontal,
3963
3997
  zIndex: 9999,
3964
- minWidth: Math.max(rect.width, PANEL_MIN_WIDTH)
3998
+ minWidth: PANEL_MIN_WIDTH,
3999
+ maxWidth: available
3965
4000
  });
3966
4001
  };
3967
4002
  reposition();
@@ -16,13 +16,25 @@
16
16
  * <ErrorBanner message="Failed to save changes." />
17
17
  * <ErrorBanner message="Draft saved." tone="info" />
18
18
  * <ErrorBanner message="This action cannot be undone." tone="warning" />
19
+ * <ErrorBanner message="Failed to save." onDismiss={clearError} />
20
+ * <ErrorBanner title="Upload failed" message="The file was too large." icon={<AlertTriangle size={18} />} action={<Button onClick={retry}>Retry</Button>} />
19
21
  */
20
22
  import type { ReactNode } from 'react';
21
23
  export type ErrorBannerTone = 'error' | 'warning' | 'info';
22
24
  export interface ErrorBannerProps {
23
25
  /** Primary message text */
24
26
  message: ReactNode;
27
+ /** Optional bold heading rendered above the message. */
28
+ title?: ReactNode;
25
29
  tone?: ErrorBannerTone;
26
30
  className?: string;
31
+ /** Optional leading icon rendered before the text. */
32
+ icon?: ReactNode;
33
+ /** Optional trailing action (e.g. a Retry button) rendered before the close button. */
34
+ action?: ReactNode;
35
+ /** When provided, renders a close button that invokes this handler. */
36
+ onDismiss?: () => void;
37
+ /** Accessible label for the close button. Defaults to "Dismiss". */
38
+ dismissLabel?: string;
27
39
  }
28
- export declare function ErrorBanner({ message, tone, className }: ErrorBannerProps): import("react").JSX.Element;
40
+ export declare function ErrorBanner({ message, title, tone, className, icon, action, onDismiss, dismissLabel, }: ErrorBannerProps): import("react").JSX.Element;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@diwauhris/ui",
3
3
  "private": false,
4
- "version": "1.7.21",
4
+ "version": "1.7.23",
5
5
  "type": "module",
6
6
  "description": "DIWA UHRIS UI component library — React + Tailwind CSS",
7
7
  "keywords": [