@aurora-ds/components 0.18.2 → 0.19.0

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.
Files changed (49) hide show
  1. package/README.md +1 -0
  2. package/dist/cjs/components/data-display/avatar/Avatar.props.d.ts +1 -1
  3. package/dist/cjs/components/forms/date-picker/calendar/Calendar.props.d.ts +2 -2
  4. package/dist/cjs/components/index.d.ts +2 -0
  5. package/dist/cjs/components/overlay/alert/Alert.d.ts +32 -0
  6. package/dist/cjs/components/overlay/alert/Alert.props.d.ts +56 -0
  7. package/dist/cjs/components/overlay/alert/Alert.styles.d.ts +4 -0
  8. package/dist/cjs/components/overlay/alert/index.d.ts +2 -0
  9. package/dist/cjs/constants/globalConstants.d.ts +1 -0
  10. package/dist/cjs/hooks/index.d.ts +2 -0
  11. package/dist/cjs/hooks/useAlert.d.ts +23 -0
  12. package/dist/cjs/hooks/useAlert.types.d.ts +56 -0
  13. package/dist/cjs/index.js +336 -2
  14. package/dist/cjs/index.js.map +1 -1
  15. package/dist/cjs/interfaces/alert.types.d.ts +8 -0
  16. package/dist/cjs/interfaces/index.d.ts +1 -0
  17. package/dist/cjs/resources/Icons.d.ts +4 -1
  18. package/dist/cjs/resources/icons/AlertCircleIcon.d.ts +2 -0
  19. package/dist/cjs/resources/icons/AlertTriangleIcon.d.ts +2 -0
  20. package/dist/cjs/resources/icons/CheckCircleIcon.d.ts +2 -0
  21. package/dist/cjs/utils/ui/components/foundation/text/getTruncateTextStyles.utils.d.ts +5 -1
  22. package/dist/cjs/utils/ui/components/overlay/alert/getAlertIcon.utils.d.ts +8 -0
  23. package/dist/cjs/utils/ui/components/overlay/alert/getAlertPositionStyles.utils.d.ts +8 -0
  24. package/dist/cjs/utils/ui/components/overlay/alert/getAlertVariantColors.utils.d.ts +14 -0
  25. package/dist/esm/components/data-display/avatar/Avatar.props.d.ts +1 -1
  26. package/dist/esm/components/forms/date-picker/calendar/Calendar.props.d.ts +2 -2
  27. package/dist/esm/components/index.d.ts +2 -0
  28. package/dist/esm/components/overlay/alert/Alert.d.ts +32 -0
  29. package/dist/esm/components/overlay/alert/Alert.props.d.ts +56 -0
  30. package/dist/esm/components/overlay/alert/Alert.styles.d.ts +4 -0
  31. package/dist/esm/components/overlay/alert/index.d.ts +2 -0
  32. package/dist/esm/constants/globalConstants.d.ts +1 -0
  33. package/dist/esm/hooks/index.d.ts +2 -0
  34. package/dist/esm/hooks/useAlert.d.ts +23 -0
  35. package/dist/esm/hooks/useAlert.types.d.ts +56 -0
  36. package/dist/esm/index.js +335 -4
  37. package/dist/esm/index.js.map +1 -1
  38. package/dist/esm/interfaces/alert.types.d.ts +8 -0
  39. package/dist/esm/interfaces/index.d.ts +1 -0
  40. package/dist/esm/resources/Icons.d.ts +4 -1
  41. package/dist/esm/resources/icons/AlertCircleIcon.d.ts +2 -0
  42. package/dist/esm/resources/icons/AlertTriangleIcon.d.ts +2 -0
  43. package/dist/esm/resources/icons/CheckCircleIcon.d.ts +2 -0
  44. package/dist/esm/utils/ui/components/foundation/text/getTruncateTextStyles.utils.d.ts +5 -1
  45. package/dist/esm/utils/ui/components/overlay/alert/getAlertIcon.utils.d.ts +8 -0
  46. package/dist/esm/utils/ui/components/overlay/alert/getAlertPositionStyles.utils.d.ts +8 -0
  47. package/dist/esm/utils/ui/components/overlay/alert/getAlertVariantColors.utils.d.ts +14 -0
  48. package/dist/index.d.ts +153 -3
  49. package/package.json +1 -1
package/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
- import React, { Children, isValidElement, cloneElement, createElement, Fragment, useMemo, memo, useCallback, forwardRef, useState, useRef, useEffect, useLayoutEffect } from 'react';
2
+ import React, { Children, isValidElement, cloneElement, createElement, Fragment, useMemo, memo, useCallback, forwardRef, useState, useRef, useEffect, useLayoutEffect, createContext, useContext } from 'react';
3
3
  import { createStyles, useTheme, keyframes, colors } from '@aurora-ds/theme';
4
4
  import { createPortal } from 'react-dom';
5
5
 
@@ -141,6 +141,10 @@ const getTruncateTextStyles = (maxLines) => (maxLines === 1
141
141
  display: '-webkit-box',
142
142
  WebkitLineClamp: maxLines,
143
143
  WebkitBoxOrient: 'vertical',
144
+ // Improve wrapping behavior for multi-line clamp compatibility
145
+ whiteSpace: 'normal',
146
+ overflowWrap: 'break-word',
147
+ wordBreak: 'break-word',
144
148
  lineClamp: maxLines,
145
149
  });
146
150
 
@@ -216,8 +220,11 @@ const Text = ({ children, variant = 'span', color, fontSize, fontFamily, maxLine
216
220
  const variantStyles = useMemo(() => getTextVariantStyles(theme), [theme]);
217
221
  const tag = variantStyles[variant].tag;
218
222
  const parsedChildren = useMemo(() => parseTextWithBold(children), [children]);
223
+ // Force inline truncate styles when needed (fix for multi-line clamp not applied in some envs)
224
+ const truncateStyles = maxLines ? getTruncateTextStyles(maxLines) : undefined;
219
225
  return createElement(tag, {
220
226
  className: TEXT_STYLES.root({ variant, color, fontSize, fontFamily, maxLines, underline, preserveWhitespace }),
227
+ style: truncateStyles,
221
228
  'aria-label': ariaLabel,
222
229
  'aria-labelledby': ariaLabelledBy,
223
230
  'aria-describedby': ariaDescribedBy,
@@ -418,6 +425,7 @@ const Chip = ({ label, icon, variant = 'filled', color = 'default', size = 'md',
418
425
  Chip.displayName = 'Chip';
419
426
 
420
427
  const BUTTON_SIZE = 36;
428
+ const ALERT_MAX_WIDTH = 320;
421
429
  const MENU_ITEM_SIZE = 32;
422
430
  const DRAWER_ITEM_HEIGHT = 32;
423
431
  const DEFAULT_TRANSITION_DURATION_MS = 150;
@@ -476,7 +484,11 @@ const Avatar = ({ image, label, onClick, size = 'medium', color, borderColor, ba
476
484
  const AVATAR_SIZES = getAvatarSizes(theme);
477
485
  const hasImage = !!image;
478
486
  const clickable = !!onClick;
479
- return (jsx("div", { className: AVATAR_STYLES.root({ hasImage, clickable, size, color, borderColor, backgroundColor }), onClick: onClick, children: hasImage ? (jsx("img", { src: image, alt: label || 'Avatar', className: AVATAR_STYLES.image })) : (jsx(Text, { variant: 'label', fontSize: AVATAR_SIZES[size].fontSize, children: label || '?' })) }));
487
+ return (jsx("div", { className: AVATAR_STYLES.root({ hasImage, clickable, size, color, borderColor, backgroundColor }), onClick: (event) => {
488
+ if (onClick) {
489
+ onClick(event);
490
+ }
491
+ }, children: hasImage ? (jsx("img", { src: image, alt: label || 'Avatar', className: AVATAR_STYLES.image })) : (jsx(Text, { variant: 'label', fontSize: AVATAR_SIZES[size].fontSize, children: label || '?' })) }));
480
492
  };
481
493
  Avatar.displayName = 'Avatar';
482
494
 
@@ -935,10 +947,22 @@ const Stack = ({ children, direction = 'row', gap = 'sm', width, height, align =
935
947
  };
936
948
  Stack.displayName = 'Stack';
937
949
 
950
+ const AlertCircleIcon = () => {
951
+ return (jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '100%', height: '100%', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', children: [jsx("circle", { cx: '12', cy: '12', r: '10' }), jsx("line", { x1: '12', y1: '8', x2: '12', y2: '12' }), jsx("line", { x1: '12', y1: '16', x2: '12.01', y2: '16' })] }));
952
+ };
953
+
954
+ const AlertTriangleIcon = () => {
955
+ return (jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '100%', height: '100%', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', children: [jsx("path", { d: 'M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z' }), jsx("line", { x1: '12', y1: '9', x2: '12', y2: '13' }), jsx("line", { x1: '12', y1: '17', x2: '12.01', y2: '17' })] }));
956
+ };
957
+
938
958
  const CalendarIcon = () => {
939
959
  return (jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', children: [jsx("path", { d: 'M8 2v4' }), jsx("path", { d: 'M16 2v4' }), jsx("rect", { width: '18', height: '18', x: '3', y: '4', rx: '2' }), jsx("path", { d: 'M3 10h18' })] }));
940
960
  };
941
961
 
962
+ const CheckCircleIcon = () => {
963
+ return (jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '100%', height: '100%', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', children: [jsx("circle", { cx: '12', cy: '12', r: '10' }), jsx("polyline", { points: '9 12 11 14 15 10' })] }));
964
+ };
965
+
942
966
  const ChevronDownIcon = () => {
943
967
  return (jsx("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '100%', height: '100%', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: 'lucide lucide-chevron-down-icon lucide-chevron-down', children: jsx("path", { d: 'm6 9 6 6 6-6' }) }));
944
968
  };
@@ -961,6 +985,10 @@ const EyeOffIcon = () => {
961
985
  return (jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: 'lucide lucide-eye-off-icon lucide-eye-off', children: [jsx("path", { d: 'M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49' }), jsx("path", { d: 'M14.084 14.158a3 3 0 0 1-4.242-4.242' }), jsx("path", { d: 'M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143' }), jsx("path", { d: 'm2 2 20 20' })] }));
962
986
  };
963
987
 
988
+ const InfoIcon = () => {
989
+ return (jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '100%', height: '100%', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', children: [jsx("circle", { cx: '12', cy: '12', r: '10' }), jsx("line", { x1: '12', y1: '16', x2: '12', y2: '12' }), jsx("line", { x1: '12', y1: '8', x2: '12.01', y2: '8' })] }));
990
+ };
991
+
964
992
  const MoreHorizontalIcon = () => {
965
993
  return (jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round', className: 'lucide lucide-ellipsis-icon lucide-ellipsis', children: [jsx("circle", { cx: '12', cy: '12', r: '1' }), jsx("circle", { cx: '19', cy: '12', r: '1' }), jsx("circle", { cx: '5', cy: '12', r: '1' })] }));
966
994
  };
@@ -1667,7 +1695,7 @@ const Calendar$1 = ({ value, onDateSelect, minDate, maxDate, locale = 'fr-FR', }
1667
1695
  const year = value?.getFullYear() ?? new Date().getFullYear();
1668
1696
  return Math.floor(year / 12) * 12;
1669
1697
  });
1670
- const calendarDays = useMemo(() => getCalendarDays(currentMonth.getFullYear(), currentMonth.getMonth(), value, minDate, maxDate), [currentMonth, value, minDate, maxDate]);
1698
+ const calendarDays = useMemo(() => getCalendarDays(currentMonth.getFullYear(), currentMonth.getMonth(), value, minDate ?? undefined, maxDate ?? undefined), [currentMonth, value, minDate, maxDate]);
1671
1699
  const headerLabel = useMemo(() => {
1672
1700
  if (view === 'days') {
1673
1701
  return currentMonth.toLocaleDateString(locale, { month: 'long', year: 'numeric' });
@@ -2129,6 +2157,160 @@ const Accordion = ({ title, children, expanded, defaultExpanded = false, onChang
2129
2157
  };
2130
2158
  Accordion.displayName = 'Accordion';
2131
2159
 
2160
+ const ALERT_OFFSET = 16;
2161
+ /**
2162
+ * Get position styles for alert
2163
+ * @param position - Alert position
2164
+ * @param offsetY - Vertical offset for stacking (in pixels)
2165
+ * @returns Object with CSS position styles (top/bottom + left/right)
2166
+ */
2167
+ const getAlertPositionStyles = (position, offsetY) => {
2168
+ const verticalPosition = `${ALERT_OFFSET + offsetY}px`;
2169
+ switch (position) {
2170
+ case 'top-left':
2171
+ return { top: verticalPosition, left: `${ALERT_OFFSET}px` };
2172
+ case 'top-right':
2173
+ return { top: verticalPosition, right: `${ALERT_OFFSET}px` };
2174
+ case 'bottom-left':
2175
+ return { bottom: verticalPosition, left: `${ALERT_OFFSET}px` };
2176
+ case 'bottom-right':
2177
+ return { bottom: verticalPosition, right: `${ALERT_OFFSET}px` };
2178
+ default:
2179
+ return { top: verticalPosition, right: `${ALERT_OFFSET}px` };
2180
+ }
2181
+ };
2182
+
2183
+ /**
2184
+ * Get colors for alert variants
2185
+ * @param theme - Aurora theme
2186
+ * @param variant - Alert variant
2187
+ * @returns Colors for the variant
2188
+ */
2189
+ const getAlertVariantColors = (theme, variant) => {
2190
+ const variantColors = {
2191
+ default: {
2192
+ background: theme.colors.surface,
2193
+ border: theme.colors.border,
2194
+ iconColor: 'text'
2195
+ },
2196
+ info: {
2197
+ background: theme.colors.infoSubtle,
2198
+ border: theme.colors.info,
2199
+ iconColor: 'info'
2200
+ },
2201
+ warning: {
2202
+ background: theme.colors.warningSubtle,
2203
+ border: theme.colors.warning,
2204
+ iconColor: 'warning'
2205
+ },
2206
+ error: {
2207
+ background: theme.colors.errorSubtle,
2208
+ border: theme.colors.error,
2209
+ iconColor: 'error'
2210
+ },
2211
+ success: {
2212
+ background: theme.colors.successSubtle,
2213
+ border: theme.colors.success,
2214
+ iconColor: 'success'
2215
+ }
2216
+ };
2217
+ return variantColors[variant];
2218
+ };
2219
+
2220
+ const ALERT_STYLES = createStyles((theme) => ({
2221
+ root: ({ variant, position, isVisible, offsetY, maxWidth }) => {
2222
+ const colors = getAlertVariantColors(theme, variant);
2223
+ const positionStyles = getAlertPositionStyles(position, offsetY);
2224
+ return {
2225
+ position: 'fixed',
2226
+ ...positionStyles,
2227
+ display: 'flex',
2228
+ alignItems: 'center',
2229
+ gap: theme.spacing.sm,
2230
+ padding: theme.spacing.sm,
2231
+ backgroundColor: colors.background,
2232
+ border: `1px solid ${colors.border}`,
2233
+ borderRadius: theme.radius.md,
2234
+ boxShadow: theme.shadows.lg,
2235
+ minWidth: ALERT_MAX_WIDTH,
2236
+ maxWidth,
2237
+ overflow: 'hidden',
2238
+ zIndex: theme.zIndex.toast,
2239
+ opacity: isVisible ? 1 : 0,
2240
+ transform: isVisible ? 'translateY(0)' :
2241
+ (position.startsWith('top') ? 'translateY(-8px)' : 'translateY(8px)'),
2242
+ transition: `all ${theme.transition.normal}, opacity ${theme.transition.fast}`,
2243
+ pointerEvents: isVisible ? 'auto' : 'none'
2244
+ };
2245
+ }
2246
+ }));
2247
+
2248
+ // Pre-create icon instances to avoid recreation on every call
2249
+ const ALERT_ICONS = {
2250
+ default: jsx(InfoIcon, {}),
2251
+ info: jsx(InfoIcon, {}),
2252
+ warning: jsx(AlertTriangleIcon, {}),
2253
+ error: jsx(AlertCircleIcon, {}),
2254
+ success: jsx(CheckCircleIcon, {})
2255
+ };
2256
+ /**
2257
+ * Get icon component for alert variant
2258
+ * @param variant - Alert variant
2259
+ * @returns JSX Element for the icon
2260
+ */
2261
+ const getAlertIcon = (variant) => {
2262
+ return ALERT_ICONS[variant];
2263
+ };
2264
+
2265
+ /**
2266
+ * Alert component - Display notifications with different variants
2267
+ *
2268
+ * **Variants:**
2269
+ * - `default`: Standard alert with neutral styling
2270
+ * - `info`: Informational alert with blue styling
2271
+ * - `warning`: Warning alert with yellow/orange styling
2272
+ * - `error`: Error alert with red styling
2273
+ * - `success`: Success alert with green styling
2274
+ *
2275
+ * **Features:**
2276
+ * - Auto-dismisses after 3 seconds
2277
+ * - Positioned at screen corners
2278
+ * - Supports stacking with offsetY
2279
+ * - Icon based on variant
2280
+ * - Smooth animations
2281
+ * - Dynamic height calculation for proper stacking
2282
+ *
2283
+ * @example
2284
+ * ```tsx
2285
+ * <Alert
2286
+ * text="Operation completed successfully"
2287
+ * variant="success"
2288
+ * position="top-right"
2289
+ * isVisible={true}
2290
+ * />
2291
+ * ```
2292
+ */
2293
+ const Alert = memo(({ text, variant = 'default', position = 'top-right', isVisible = false, offsetY = 0, maxWidth = ALERT_MAX_WIDTH, alertId, onHeightChange }) => {
2294
+ const theme = useTheme();
2295
+ const alertRef = useRef(null);
2296
+ const lastHeightRef = useRef(0);
2297
+ const icon = useMemo(() => getAlertIcon(variant), [variant]);
2298
+ const colors = useMemo(() => getAlertVariantColors(theme, variant), [theme, variant]);
2299
+ // Report height changes to parent (only when height actually changes)
2300
+ useEffect(() => {
2301
+ if (alertRef.current && onHeightChange && isVisible && alertId) {
2302
+ const height = alertRef.current.offsetHeight;
2303
+ // Only call onHeightChange if height has actually changed
2304
+ if (height !== lastHeightRef.current) {
2305
+ lastHeightRef.current = height;
2306
+ onHeightChange(alertId, height);
2307
+ }
2308
+ }
2309
+ }, [isVisible, text, maxWidth, alertId, onHeightChange]);
2310
+ return (jsxs("div", { ref: alertRef, className: ALERT_STYLES.root({ variant, position, isVisible, offsetY, maxWidth }), role: 'alert', "aria-live": 'polite', children: [jsx(Icon, { size: 'sm', color: colors.iconColor, children: icon }), jsx(Text, { variant: 'span', fontSize: 'sm', color: colors.iconColor, maxLines: 4, children: text })] }));
2311
+ });
2312
+ Alert.displayName = 'Alert';
2313
+
2132
2314
  const MODAL_STYLES = createStyles((theme) => ({
2133
2315
  background: (isFadingIn) => ({
2134
2316
  background: 'rgba(0, 0, 0, 0.6)',
@@ -2639,5 +2821,154 @@ const Pagination = ({ currentPage, totalPages, onPageChange, onPrevious, onNext,
2639
2821
  };
2640
2822
  Pagination.displayName = 'Pagination';
2641
2823
 
2642
- export { Accordion, Avatar, AvatarGroup, Breadcrumb, BreadcrumbEllipsis, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, Button, Card, Chip, DatePicker_default as DatePicker, DrawerItem, Form, Grid, Icon, IconButton, Input_default as Input, Menu, MenuGroup, MenuItem, Modal, Page, PageSection, Pagination, Select, Separator, Skeleton, Stack, TabItem, Tabs, Text, TextArea_default as TextArea, useAnchorPosition, useClickOutside, useTransitionRender };
2824
+ const AlertContext = createContext(undefined);
2825
+ const ALERT_DURATION = 3000;
2826
+ const MAX_ALERTS = 2;
2827
+ const ALERT_HEIGHT = 48; // Approximate height with padding (px)
2828
+ const ALERT_SPACING = 6; // Spacing between alerts (px)
2829
+ /**
2830
+ * Alert Provider Component
2831
+ * Manages alert queue and renders alerts in a portal
2832
+ */
2833
+ const AlertProvider = ({ children }) => {
2834
+ const [alerts, setAlerts] = useState([]);
2835
+ const [visibleAlerts, setVisibleAlerts] = useState(new Set());
2836
+ const [alertHeights, setAlertHeights] = useState(new Map());
2837
+ const timersRef = useRef(new Map());
2838
+ const showAlert = useCallback((options) => {
2839
+ const { text, variant = 'default', position = 'top-right', duration = ALERT_DURATION, maxWidth = 300 } = options;
2840
+ const id = `alert-${Date.now()}-${Math.random()}`;
2841
+ const newAlert = {
2842
+ id,
2843
+ text,
2844
+ variant,
2845
+ position,
2846
+ timestamp: Date.now(),
2847
+ maxWidth
2848
+ };
2849
+ setAlerts((prev) => {
2850
+ const updatedAlerts = [...prev, newAlert];
2851
+ // Keep only MAX_ALERTS for the same position
2852
+ const samePositionAlerts = updatedAlerts.filter(a => a.position === position);
2853
+ if (samePositionAlerts.length > MAX_ALERTS) {
2854
+ // Remove the oldest alert for this position
2855
+ const oldestAlert = samePositionAlerts[0];
2856
+ const timerToRemove = timersRef.current.get(oldestAlert.id);
2857
+ if (timerToRemove) {
2858
+ clearTimeout(timerToRemove);
2859
+ timersRef.current.delete(oldestAlert.id);
2860
+ }
2861
+ setVisibleAlerts((prevVisible) => {
2862
+ const newVisible = new Set(prevVisible);
2863
+ newVisible.delete(oldestAlert.id);
2864
+ return newVisible;
2865
+ });
2866
+ // Remove from alerts array after animation
2867
+ setTimeout(() => {
2868
+ setAlerts((current) => current.filter(a => a.id !== oldestAlert.id));
2869
+ setAlertHeights((prevHeights) => {
2870
+ const newMap = new Map(prevHeights);
2871
+ newMap.delete(oldestAlert.id);
2872
+ return newMap;
2873
+ });
2874
+ }, 300);
2875
+ return updatedAlerts.filter(a => a.id !== oldestAlert.id);
2876
+ }
2877
+ return updatedAlerts;
2878
+ });
2879
+ // Show alert after a small delay for animation
2880
+ setTimeout(() => {
2881
+ setVisibleAlerts((prev) => new Set(prev).add(id));
2882
+ }, 10);
2883
+ // Auto-dismiss timer
2884
+ const timer = setTimeout(() => {
2885
+ setVisibleAlerts((prev) => {
2886
+ const newVisible = new Set(prev);
2887
+ newVisible.delete(id);
2888
+ return newVisible;
2889
+ });
2890
+ // Remove from DOM after animation
2891
+ setTimeout(() => {
2892
+ setAlerts((prev) => prev.filter(a => a.id !== id));
2893
+ setAlertHeights((prev) => {
2894
+ const newMap = new Map(prev);
2895
+ newMap.delete(id);
2896
+ return newMap;
2897
+ });
2898
+ timersRef.current.delete(id);
2899
+ }, 300);
2900
+ }, duration);
2901
+ timersRef.current.set(id, timer);
2902
+ }, []);
2903
+ // Handle height changes from alerts (memoized to avoid re-creating on every render)
2904
+ const handleHeightChange = useCallback((alertId, height) => {
2905
+ setAlertHeights((prev) => {
2906
+ const currentHeight = prev.get(alertId);
2907
+ // Only update if height actually changed
2908
+ if (currentHeight !== height) {
2909
+ const newMap = new Map(prev);
2910
+ newMap.set(alertId, height);
2911
+ return newMap;
2912
+ }
2913
+ return prev;
2914
+ });
2915
+ }, []);
2916
+ // Cleanup timers on unmount
2917
+ useEffect(() => {
2918
+ return () => {
2919
+ timersRef.current.forEach(timer => clearTimeout(timer));
2920
+ timersRef.current.clear();
2921
+ };
2922
+ }, []);
2923
+ const contextValue = useMemo(() => ({ showAlert }), [showAlert]);
2924
+ // Group alerts by position
2925
+ const alertsByPosition = useMemo(() => {
2926
+ const grouped = {};
2927
+ alerts.forEach(alert => {
2928
+ if (!grouped[alert.position]) {
2929
+ grouped[alert.position] = [];
2930
+ }
2931
+ grouped[alert.position].push(alert);
2932
+ });
2933
+ return grouped;
2934
+ }, [alerts]);
2935
+ return (jsxs(AlertContext.Provider, { value: contextValue, children: [children, typeof window !== 'undefined' && createPortal(jsx(Fragment$1, { children: Object.entries(alertsByPosition).map(([position, positionAlerts]) => {
2936
+ // positionAlerts is ordered from oldest -> newest
2937
+ const isTopPosition = position.startsWith('top');
2938
+ const ordered = isTopPosition ? [...positionAlerts].reverse() : positionAlerts;
2939
+ return ordered.map((alert, index) => {
2940
+ // Calculate offset based on actual heights of previous alerts
2941
+ let offset = 0;
2942
+ for (let i = 0; i < index; i++) {
2943
+ const prevAlert = ordered[i];
2944
+ const height = alertHeights.get(prevAlert.id) || ALERT_HEIGHT;
2945
+ offset += height + ALERT_SPACING;
2946
+ }
2947
+ return (jsx(Alert, { text: alert.text, variant: alert.variant, position: alert.position, isVisible: visibleAlerts.has(alert.id), offsetY: offset, maxWidth: alert.maxWidth, alertId: alert.id, onHeightChange: handleHeightChange }, alert.id));
2948
+ });
2949
+ }) }), document.body)] }));
2950
+ };
2951
+ /**
2952
+ * Hook to access alert functionality
2953
+ *
2954
+ * @example
2955
+ * ```tsx
2956
+ * const { showAlert } = useAlert()
2957
+ *
2958
+ * showAlert({
2959
+ * text: 'Operation completed',
2960
+ * variant: 'success',
2961
+ * position: 'top-right'
2962
+ * })
2963
+ * ```
2964
+ */
2965
+ const useAlert = () => {
2966
+ const context = useContext(AlertContext);
2967
+ if (!context) {
2968
+ throw new Error('useAlert must be used within an AlertProvider');
2969
+ }
2970
+ return context;
2971
+ };
2972
+
2973
+ export { Accordion, Alert, AlertProvider, Avatar, AvatarGroup, Breadcrumb, BreadcrumbEllipsis, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, Button, Card, Chip, DatePicker_default as DatePicker, DrawerItem, Form, Grid, Icon, IconButton, Input_default as Input, Menu, MenuGroup, MenuItem, Modal, Page, PageSection, Pagination, Select, Separator, Skeleton, Stack, TabItem, Tabs, Text, TextArea_default as TextArea, useAlert, useAnchorPosition, useClickOutside, useTransitionRender };
2643
2974
  //# sourceMappingURL=index.js.map