@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/README.md CHANGED
@@ -47,6 +47,7 @@ function App() {
47
47
  | Component | Description |
48
48
  |-----------|-------------|
49
49
  | **Accordion** | Collapsible container to show/hide content |
50
+ | **Alert** | Notification component with variants (default, info, warning, error, success) and auto-dismiss |
50
51
  | **Avatar** | User avatar display component with image or initials |
51
52
  | **AvatarGroup** | Group of avatars with overlapping display |
52
53
  | **Breadcrumb** | Navigation component for hierarchical page structures |
@@ -6,7 +6,7 @@ export type AvatarProps = {
6
6
  /** Fallback text to display when no image */
7
7
  label?: string;
8
8
  /** Click handler */
9
- onClick?: (e: MouseEvent<HTMLButtonElement>) => void;
9
+ onClick?: (e: MouseEvent<HTMLDivElement>) => void;
10
10
  /** Size of the avatar */
11
11
  size?: AvatarSize;
12
12
  /** Text color for the label */
@@ -5,9 +5,9 @@ export type CalendarProps = {
5
5
  /** Callback when a date is selected */
6
6
  onDateSelect: (date: Date) => void;
7
7
  /** Minimum selectable date */
8
- minDate?: Date;
8
+ minDate?: Date | null;
9
9
  /** Maximum selectable date */
10
- maxDate?: Date;
10
+ maxDate?: Date | null;
11
11
  /** Locale for month/year display */
12
12
  locale?: string;
13
13
  };
@@ -17,12 +17,14 @@ export * from '@components/layout/separator';
17
17
  export * from '@components/layout/page-construction/page-section';
18
18
  export * from '@components/layout/page-construction/page';
19
19
  export * from '@components/overlay/accordion';
20
+ export * from '@components/overlay/alert';
20
21
  export * from '@components/overlay/menu';
21
22
  export * from '@components/overlay/modal';
22
23
  export * from '@components/navigation/drawer-item';
23
24
  export * from '@components/navigation/breadcrumb';
24
25
  export * from '@components/navigation/tabs';
25
26
  export * from '@components/navigation/pagination';
27
+ export type { AlertVariant, AlertPosition } from '@interfaces/alert.types';
26
28
  export type { ButtonVariants, ButtonVariantStyle } from '@interfaces/button.types';
27
29
  export type { TextVariants, TextVariantStyle } from '@interfaces/text.types';
28
30
  export type { ChipVariant, ChipColor, ChipSize } from '@interfaces/chip.types';
@@ -0,0 +1,32 @@
1
+ import { FC } from 'react';
2
+ import { AlertProps } from '@components/overlay/alert/Alert.props';
3
+ /**
4
+ * Alert component - Display notifications with different variants
5
+ *
6
+ * **Variants:**
7
+ * - `default`: Standard alert with neutral styling
8
+ * - `info`: Informational alert with blue styling
9
+ * - `warning`: Warning alert with yellow/orange styling
10
+ * - `error`: Error alert with red styling
11
+ * - `success`: Success alert with green styling
12
+ *
13
+ * **Features:**
14
+ * - Auto-dismisses after 3 seconds
15
+ * - Positioned at screen corners
16
+ * - Supports stacking with offsetY
17
+ * - Icon based on variant
18
+ * - Smooth animations
19
+ * - Dynamic height calculation for proper stacking
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * <Alert
24
+ * text="Operation completed successfully"
25
+ * variant="success"
26
+ * position="top-right"
27
+ * isVisible={true}
28
+ * />
29
+ * ```
30
+ */
31
+ declare const Alert: FC<AlertProps>;
32
+ export { Alert };
@@ -0,0 +1,56 @@
1
+ import { AlertPosition, AlertVariant } from '@interfaces/alert.types';
2
+ /**
3
+ * Alert component props
4
+ */
5
+ export interface AlertProps {
6
+ /**
7
+ * Text content of the alert
8
+ */
9
+ text: string;
10
+ /**
11
+ * Variant of the alert determining its color and icon
12
+ * @default 'default'
13
+ */
14
+ variant?: AlertVariant;
15
+ /**
16
+ * Position of the alert on screen
17
+ * @default 'top-right'
18
+ */
19
+ position?: AlertPosition;
20
+ /**
21
+ * Whether the alert is visible
22
+ * @default false
23
+ */
24
+ isVisible?: boolean;
25
+ /**
26
+ * Vertical offset for stacking alerts (in pixels)
27
+ * @default 0
28
+ */
29
+ offsetY?: number;
30
+ /**
31
+ * Maximum width of the alert in pixels
32
+ * @default 300
33
+ */
34
+ maxWidth?: number;
35
+ /**
36
+ * Unique identifier for the alert
37
+ * @internal
38
+ */
39
+ alertId?: string;
40
+ /**
41
+ * Callback when alert height changes
42
+ * @internal
43
+ */
44
+ onHeightChange?: (alertId: string, height: number) => void;
45
+ }
46
+ /**
47
+ * Internal style parameters for Alert component
48
+ * @internal
49
+ */
50
+ export interface AlertStyleParams {
51
+ variant: AlertVariant;
52
+ position: AlertPosition;
53
+ isVisible: boolean;
54
+ offsetY: number;
55
+ maxWidth: number;
56
+ }
@@ -0,0 +1,4 @@
1
+ import { AlertStyleParams } from '@components/overlay/alert/Alert.props';
2
+ export declare const ALERT_STYLES: {
3
+ root: (args_0: AlertStyleParams) => string;
4
+ };
@@ -0,0 +1,2 @@
1
+ export * from '@components/overlay/alert/Alert';
2
+ export type { AlertProps } from '@components/overlay/alert/Alert.props';
@@ -1,4 +1,5 @@
1
1
  export declare const BUTTON_SIZE = 36;
2
+ export declare const ALERT_MAX_WIDTH = 320;
2
3
  export declare const MENU_ITEM_SIZE = 32;
3
4
  export declare const DRAWER_ITEM_HEIGHT = 32;
4
5
  export declare const DEFAULT_TRANSITION_DURATION_MS = 150;
@@ -1,3 +1,5 @@
1
+ export { useAlert, AlertProvider } from '@hooks/useAlert.tsx';
2
+ export type { ShowAlertOptions, AlertContextValue, AlertProviderProps } from '@hooks/useAlert.types';
1
3
  export { useAnchorPosition } from '@hooks/useAnchorPosition';
2
4
  export type { AnchorOrigin, AnchorPosition } from '@hooks/useAnchorPosition.types';
3
5
  export { useClickOutside } from '@hooks/useClickOutside';
@@ -0,0 +1,23 @@
1
+ import { FC } from 'react';
2
+ import { AlertContextValue, AlertProviderProps } from '@hooks/useAlert.types';
3
+ /**
4
+ * Alert Provider Component
5
+ * Manages alert queue and renders alerts in a portal
6
+ */
7
+ declare const AlertProvider: FC<AlertProviderProps>;
8
+ /**
9
+ * Hook to access alert functionality
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * const { showAlert } = useAlert()
14
+ *
15
+ * showAlert({
16
+ * text: 'Operation completed',
17
+ * variant: 'success',
18
+ * position: 'top-right'
19
+ * })
20
+ * ```
21
+ */
22
+ declare const useAlert: () => AlertContextValue;
23
+ export { AlertProvider, useAlert };
@@ -0,0 +1,56 @@
1
+ import { AlertPosition, AlertVariant } from '@interfaces/alert.types';
2
+ /**
3
+ * Alert item in the queue
4
+ */
5
+ export interface AlertItem {
6
+ id: string;
7
+ text: string;
8
+ variant: AlertVariant;
9
+ position: AlertPosition;
10
+ timestamp: number;
11
+ maxWidth: number;
12
+ }
13
+ /**
14
+ * Options for showing an alert
15
+ */
16
+ export interface ShowAlertOptions {
17
+ /**
18
+ * Text content of the alert
19
+ */
20
+ text: string;
21
+ /**
22
+ * Variant of the alert
23
+ * @default 'default'
24
+ */
25
+ variant?: AlertVariant;
26
+ /**
27
+ * Position of the alert on screen
28
+ * @default 'top-right'
29
+ */
30
+ position?: AlertPosition;
31
+ /**
32
+ * Duration before auto-dismiss in milliseconds
33
+ * @default 3000
34
+ */
35
+ duration?: number;
36
+ /**
37
+ * Maximum width of the alert in pixels
38
+ * @default 300
39
+ */
40
+ maxWidth?: number;
41
+ }
42
+ /**
43
+ * Alert context value
44
+ */
45
+ export interface AlertContextValue {
46
+ /**
47
+ * Show an alert
48
+ */
49
+ showAlert: (options: ShowAlertOptions) => void;
50
+ }
51
+ /**
52
+ * Alert provider props
53
+ */
54
+ export interface AlertProviderProps {
55
+ children: React.ReactNode;
56
+ }
package/dist/cjs/index.js CHANGED
@@ -143,6 +143,10 @@ const getTruncateTextStyles = (maxLines) => (maxLines === 1
143
143
  display: '-webkit-box',
144
144
  WebkitLineClamp: maxLines,
145
145
  WebkitBoxOrient: 'vertical',
146
+ // Improve wrapping behavior for multi-line clamp compatibility
147
+ whiteSpace: 'normal',
148
+ overflowWrap: 'break-word',
149
+ wordBreak: 'break-word',
146
150
  lineClamp: maxLines,
147
151
  });
148
152
 
@@ -218,8 +222,11 @@ const Text = ({ children, variant = 'span', color, fontSize, fontFamily, maxLine
218
222
  const variantStyles = React.useMemo(() => getTextVariantStyles(theme$1), [theme$1]);
219
223
  const tag = variantStyles[variant].tag;
220
224
  const parsedChildren = React.useMemo(() => parseTextWithBold(children), [children]);
225
+ // Force inline truncate styles when needed (fix for multi-line clamp not applied in some envs)
226
+ const truncateStyles = maxLines ? getTruncateTextStyles(maxLines) : undefined;
221
227
  return React.createElement(tag, {
222
228
  className: TEXT_STYLES.root({ variant, color, fontSize, fontFamily, maxLines, underline, preserveWhitespace }),
229
+ style: truncateStyles,
223
230
  'aria-label': ariaLabel,
224
231
  'aria-labelledby': ariaLabelledBy,
225
232
  'aria-describedby': ariaDescribedBy,
@@ -420,6 +427,7 @@ const Chip = ({ label, icon, variant = 'filled', color = 'default', size = 'md',
420
427
  Chip.displayName = 'Chip';
421
428
 
422
429
  const BUTTON_SIZE = 36;
430
+ const ALERT_MAX_WIDTH = 320;
423
431
  const MENU_ITEM_SIZE = 32;
424
432
  const DRAWER_ITEM_HEIGHT = 32;
425
433
  const DEFAULT_TRANSITION_DURATION_MS = 150;
@@ -478,7 +486,11 @@ const Avatar = ({ image, label, onClick, size = 'medium', color, borderColor, ba
478
486
  const AVATAR_SIZES = getAvatarSizes(theme$1);
479
487
  const hasImage = !!image;
480
488
  const clickable = !!onClick;
481
- return (jsxRuntime.jsx("div", { className: AVATAR_STYLES.root({ hasImage, clickable, size, color, borderColor, backgroundColor }), onClick: onClick, children: hasImage ? (jsxRuntime.jsx("img", { src: image, alt: label || 'Avatar', className: AVATAR_STYLES.image })) : (jsxRuntime.jsx(Text, { variant: 'label', fontSize: AVATAR_SIZES[size].fontSize, children: label || '?' })) }));
489
+ return (jsxRuntime.jsx("div", { className: AVATAR_STYLES.root({ hasImage, clickable, size, color, borderColor, backgroundColor }), onClick: (event) => {
490
+ if (onClick) {
491
+ onClick(event);
492
+ }
493
+ }, children: hasImage ? (jsxRuntime.jsx("img", { src: image, alt: label || 'Avatar', className: AVATAR_STYLES.image })) : (jsxRuntime.jsx(Text, { variant: 'label', fontSize: AVATAR_SIZES[size].fontSize, children: label || '?' })) }));
482
494
  };
483
495
  Avatar.displayName = 'Avatar';
484
496
 
@@ -937,10 +949,22 @@ const Stack = ({ children, direction = 'row', gap = 'sm', width, height, align =
937
949
  };
938
950
  Stack.displayName = 'Stack';
939
951
 
952
+ const AlertCircleIcon = () => {
953
+ return (jsxRuntime.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: [jsxRuntime.jsx("circle", { cx: '12', cy: '12', r: '10' }), jsxRuntime.jsx("line", { x1: '12', y1: '8', x2: '12', y2: '12' }), jsxRuntime.jsx("line", { x1: '12', y1: '16', x2: '12.01', y2: '16' })] }));
954
+ };
955
+
956
+ const AlertTriangleIcon = () => {
957
+ return (jsxRuntime.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: [jsxRuntime.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' }), jsxRuntime.jsx("line", { x1: '12', y1: '9', x2: '12', y2: '13' }), jsxRuntime.jsx("line", { x1: '12', y1: '17', x2: '12.01', y2: '17' })] }));
958
+ };
959
+
940
960
  const CalendarIcon = () => {
941
961
  return (jsxRuntime.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: [jsxRuntime.jsx("path", { d: 'M8 2v4' }), jsxRuntime.jsx("path", { d: 'M16 2v4' }), jsxRuntime.jsx("rect", { width: '18', height: '18', x: '3', y: '4', rx: '2' }), jsxRuntime.jsx("path", { d: 'M3 10h18' })] }));
942
962
  };
943
963
 
964
+ const CheckCircleIcon = () => {
965
+ return (jsxRuntime.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: [jsxRuntime.jsx("circle", { cx: '12', cy: '12', r: '10' }), jsxRuntime.jsx("polyline", { points: '9 12 11 14 15 10' })] }));
966
+ };
967
+
944
968
  const ChevronDownIcon = () => {
945
969
  return (jsxRuntime.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: jsxRuntime.jsx("path", { d: 'm6 9 6 6 6-6' }) }));
946
970
  };
@@ -963,6 +987,10 @@ const EyeOffIcon = () => {
963
987
  return (jsxRuntime.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: [jsxRuntime.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' }), jsxRuntime.jsx("path", { d: 'M14.084 14.158a3 3 0 0 1-4.242-4.242' }), jsxRuntime.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' }), jsxRuntime.jsx("path", { d: 'm2 2 20 20' })] }));
964
988
  };
965
989
 
990
+ const InfoIcon = () => {
991
+ return (jsxRuntime.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: [jsxRuntime.jsx("circle", { cx: '12', cy: '12', r: '10' }), jsxRuntime.jsx("line", { x1: '12', y1: '16', x2: '12', y2: '12' }), jsxRuntime.jsx("line", { x1: '12', y1: '8', x2: '12.01', y2: '8' })] }));
992
+ };
993
+
966
994
  const MoreHorizontalIcon = () => {
967
995
  return (jsxRuntime.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: [jsxRuntime.jsx("circle", { cx: '12', cy: '12', r: '1' }), jsxRuntime.jsx("circle", { cx: '19', cy: '12', r: '1' }), jsxRuntime.jsx("circle", { cx: '5', cy: '12', r: '1' })] }));
968
996
  };
@@ -1669,7 +1697,7 @@ const Calendar = ({ value, onDateSelect, minDate, maxDate, locale = 'fr-FR', })
1669
1697
  const year = value?.getFullYear() ?? new Date().getFullYear();
1670
1698
  return Math.floor(year / 12) * 12;
1671
1699
  });
1672
- const calendarDays = React.useMemo(() => getCalendarDays(currentMonth.getFullYear(), currentMonth.getMonth(), value, minDate, maxDate), [currentMonth, value, minDate, maxDate]);
1700
+ const calendarDays = React.useMemo(() => getCalendarDays(currentMonth.getFullYear(), currentMonth.getMonth(), value, minDate ?? undefined, maxDate ?? undefined), [currentMonth, value, minDate, maxDate]);
1673
1701
  const headerLabel = React.useMemo(() => {
1674
1702
  if (view === 'days') {
1675
1703
  return currentMonth.toLocaleDateString(locale, { month: 'long', year: 'numeric' });
@@ -2131,6 +2159,160 @@ const Accordion = ({ title, children, expanded, defaultExpanded = false, onChang
2131
2159
  };
2132
2160
  Accordion.displayName = 'Accordion';
2133
2161
 
2162
+ const ALERT_OFFSET = 16;
2163
+ /**
2164
+ * Get position styles for alert
2165
+ * @param position - Alert position
2166
+ * @param offsetY - Vertical offset for stacking (in pixels)
2167
+ * @returns Object with CSS position styles (top/bottom + left/right)
2168
+ */
2169
+ const getAlertPositionStyles = (position, offsetY) => {
2170
+ const verticalPosition = `${ALERT_OFFSET + offsetY}px`;
2171
+ switch (position) {
2172
+ case 'top-left':
2173
+ return { top: verticalPosition, left: `${ALERT_OFFSET}px` };
2174
+ case 'top-right':
2175
+ return { top: verticalPosition, right: `${ALERT_OFFSET}px` };
2176
+ case 'bottom-left':
2177
+ return { bottom: verticalPosition, left: `${ALERT_OFFSET}px` };
2178
+ case 'bottom-right':
2179
+ return { bottom: verticalPosition, right: `${ALERT_OFFSET}px` };
2180
+ default:
2181
+ return { top: verticalPosition, right: `${ALERT_OFFSET}px` };
2182
+ }
2183
+ };
2184
+
2185
+ /**
2186
+ * Get colors for alert variants
2187
+ * @param theme - Aurora theme
2188
+ * @param variant - Alert variant
2189
+ * @returns Colors for the variant
2190
+ */
2191
+ const getAlertVariantColors = (theme, variant) => {
2192
+ const variantColors = {
2193
+ default: {
2194
+ background: theme.colors.surface,
2195
+ border: theme.colors.border,
2196
+ iconColor: 'text'
2197
+ },
2198
+ info: {
2199
+ background: theme.colors.infoSubtle,
2200
+ border: theme.colors.info,
2201
+ iconColor: 'info'
2202
+ },
2203
+ warning: {
2204
+ background: theme.colors.warningSubtle,
2205
+ border: theme.colors.warning,
2206
+ iconColor: 'warning'
2207
+ },
2208
+ error: {
2209
+ background: theme.colors.errorSubtle,
2210
+ border: theme.colors.error,
2211
+ iconColor: 'error'
2212
+ },
2213
+ success: {
2214
+ background: theme.colors.successSubtle,
2215
+ border: theme.colors.success,
2216
+ iconColor: 'success'
2217
+ }
2218
+ };
2219
+ return variantColors[variant];
2220
+ };
2221
+
2222
+ const ALERT_STYLES = theme.createStyles((theme) => ({
2223
+ root: ({ variant, position, isVisible, offsetY, maxWidth }) => {
2224
+ const colors = getAlertVariantColors(theme, variant);
2225
+ const positionStyles = getAlertPositionStyles(position, offsetY);
2226
+ return {
2227
+ position: 'fixed',
2228
+ ...positionStyles,
2229
+ display: 'flex',
2230
+ alignItems: 'center',
2231
+ gap: theme.spacing.sm,
2232
+ padding: theme.spacing.sm,
2233
+ backgroundColor: colors.background,
2234
+ border: `1px solid ${colors.border}`,
2235
+ borderRadius: theme.radius.md,
2236
+ boxShadow: theme.shadows.lg,
2237
+ minWidth: ALERT_MAX_WIDTH,
2238
+ maxWidth,
2239
+ overflow: 'hidden',
2240
+ zIndex: theme.zIndex.toast,
2241
+ opacity: isVisible ? 1 : 0,
2242
+ transform: isVisible ? 'translateY(0)' :
2243
+ (position.startsWith('top') ? 'translateY(-8px)' : 'translateY(8px)'),
2244
+ transition: `all ${theme.transition.normal}, opacity ${theme.transition.fast}`,
2245
+ pointerEvents: isVisible ? 'auto' : 'none'
2246
+ };
2247
+ }
2248
+ }));
2249
+
2250
+ // Pre-create icon instances to avoid recreation on every call
2251
+ const ALERT_ICONS = {
2252
+ default: jsxRuntime.jsx(InfoIcon, {}),
2253
+ info: jsxRuntime.jsx(InfoIcon, {}),
2254
+ warning: jsxRuntime.jsx(AlertTriangleIcon, {}),
2255
+ error: jsxRuntime.jsx(AlertCircleIcon, {}),
2256
+ success: jsxRuntime.jsx(CheckCircleIcon, {})
2257
+ };
2258
+ /**
2259
+ * Get icon component for alert variant
2260
+ * @param variant - Alert variant
2261
+ * @returns JSX Element for the icon
2262
+ */
2263
+ const getAlertIcon = (variant) => {
2264
+ return ALERT_ICONS[variant];
2265
+ };
2266
+
2267
+ /**
2268
+ * Alert component - Display notifications with different variants
2269
+ *
2270
+ * **Variants:**
2271
+ * - `default`: Standard alert with neutral styling
2272
+ * - `info`: Informational alert with blue styling
2273
+ * - `warning`: Warning alert with yellow/orange styling
2274
+ * - `error`: Error alert with red styling
2275
+ * - `success`: Success alert with green styling
2276
+ *
2277
+ * **Features:**
2278
+ * - Auto-dismisses after 3 seconds
2279
+ * - Positioned at screen corners
2280
+ * - Supports stacking with offsetY
2281
+ * - Icon based on variant
2282
+ * - Smooth animations
2283
+ * - Dynamic height calculation for proper stacking
2284
+ *
2285
+ * @example
2286
+ * ```tsx
2287
+ * <Alert
2288
+ * text="Operation completed successfully"
2289
+ * variant="success"
2290
+ * position="top-right"
2291
+ * isVisible={true}
2292
+ * />
2293
+ * ```
2294
+ */
2295
+ const Alert = React.memo(({ text, variant = 'default', position = 'top-right', isVisible = false, offsetY = 0, maxWidth = ALERT_MAX_WIDTH, alertId, onHeightChange }) => {
2296
+ const theme$1 = theme.useTheme();
2297
+ const alertRef = React.useRef(null);
2298
+ const lastHeightRef = React.useRef(0);
2299
+ const icon = React.useMemo(() => getAlertIcon(variant), [variant]);
2300
+ const colors = React.useMemo(() => getAlertVariantColors(theme$1, variant), [theme$1, variant]);
2301
+ // Report height changes to parent (only when height actually changes)
2302
+ React.useEffect(() => {
2303
+ if (alertRef.current && onHeightChange && isVisible && alertId) {
2304
+ const height = alertRef.current.offsetHeight;
2305
+ // Only call onHeightChange if height has actually changed
2306
+ if (height !== lastHeightRef.current) {
2307
+ lastHeightRef.current = height;
2308
+ onHeightChange(alertId, height);
2309
+ }
2310
+ }
2311
+ }, [isVisible, text, maxWidth, alertId, onHeightChange]);
2312
+ return (jsxRuntime.jsxs("div", { ref: alertRef, className: ALERT_STYLES.root({ variant, position, isVisible, offsetY, maxWidth }), role: 'alert', "aria-live": 'polite', children: [jsxRuntime.jsx(Icon, { size: 'sm', color: colors.iconColor, children: icon }), jsxRuntime.jsx(Text, { variant: 'span', fontSize: 'sm', color: colors.iconColor, maxLines: 4, children: text })] }));
2313
+ });
2314
+ Alert.displayName = 'Alert';
2315
+
2134
2316
  const MODAL_STYLES = theme.createStyles((theme) => ({
2135
2317
  background: (isFadingIn) => ({
2136
2318
  background: 'rgba(0, 0, 0, 0.6)',
@@ -2641,7 +2823,158 @@ const Pagination = ({ currentPage, totalPages, onPageChange, onPrevious, onNext,
2641
2823
  };
2642
2824
  Pagination.displayName = 'Pagination';
2643
2825
 
2826
+ const AlertContext = React.createContext(undefined);
2827
+ const ALERT_DURATION = 3000;
2828
+ const MAX_ALERTS = 2;
2829
+ const ALERT_HEIGHT = 48; // Approximate height with padding (px)
2830
+ const ALERT_SPACING = 6; // Spacing between alerts (px)
2831
+ /**
2832
+ * Alert Provider Component
2833
+ * Manages alert queue and renders alerts in a portal
2834
+ */
2835
+ const AlertProvider = ({ children }) => {
2836
+ const [alerts, setAlerts] = React.useState([]);
2837
+ const [visibleAlerts, setVisibleAlerts] = React.useState(new Set());
2838
+ const [alertHeights, setAlertHeights] = React.useState(new Map());
2839
+ const timersRef = React.useRef(new Map());
2840
+ const showAlert = React.useCallback((options) => {
2841
+ const { text, variant = 'default', position = 'top-right', duration = ALERT_DURATION, maxWidth = 300 } = options;
2842
+ const id = `alert-${Date.now()}-${Math.random()}`;
2843
+ const newAlert = {
2844
+ id,
2845
+ text,
2846
+ variant,
2847
+ position,
2848
+ timestamp: Date.now(),
2849
+ maxWidth
2850
+ };
2851
+ setAlerts((prev) => {
2852
+ const updatedAlerts = [...prev, newAlert];
2853
+ // Keep only MAX_ALERTS for the same position
2854
+ const samePositionAlerts = updatedAlerts.filter(a => a.position === position);
2855
+ if (samePositionAlerts.length > MAX_ALERTS) {
2856
+ // Remove the oldest alert for this position
2857
+ const oldestAlert = samePositionAlerts[0];
2858
+ const timerToRemove = timersRef.current.get(oldestAlert.id);
2859
+ if (timerToRemove) {
2860
+ clearTimeout(timerToRemove);
2861
+ timersRef.current.delete(oldestAlert.id);
2862
+ }
2863
+ setVisibleAlerts((prevVisible) => {
2864
+ const newVisible = new Set(prevVisible);
2865
+ newVisible.delete(oldestAlert.id);
2866
+ return newVisible;
2867
+ });
2868
+ // Remove from alerts array after animation
2869
+ setTimeout(() => {
2870
+ setAlerts((current) => current.filter(a => a.id !== oldestAlert.id));
2871
+ setAlertHeights((prevHeights) => {
2872
+ const newMap = new Map(prevHeights);
2873
+ newMap.delete(oldestAlert.id);
2874
+ return newMap;
2875
+ });
2876
+ }, 300);
2877
+ return updatedAlerts.filter(a => a.id !== oldestAlert.id);
2878
+ }
2879
+ return updatedAlerts;
2880
+ });
2881
+ // Show alert after a small delay for animation
2882
+ setTimeout(() => {
2883
+ setVisibleAlerts((prev) => new Set(prev).add(id));
2884
+ }, 10);
2885
+ // Auto-dismiss timer
2886
+ const timer = setTimeout(() => {
2887
+ setVisibleAlerts((prev) => {
2888
+ const newVisible = new Set(prev);
2889
+ newVisible.delete(id);
2890
+ return newVisible;
2891
+ });
2892
+ // Remove from DOM after animation
2893
+ setTimeout(() => {
2894
+ setAlerts((prev) => prev.filter(a => a.id !== id));
2895
+ setAlertHeights((prev) => {
2896
+ const newMap = new Map(prev);
2897
+ newMap.delete(id);
2898
+ return newMap;
2899
+ });
2900
+ timersRef.current.delete(id);
2901
+ }, 300);
2902
+ }, duration);
2903
+ timersRef.current.set(id, timer);
2904
+ }, []);
2905
+ // Handle height changes from alerts (memoized to avoid re-creating on every render)
2906
+ const handleHeightChange = React.useCallback((alertId, height) => {
2907
+ setAlertHeights((prev) => {
2908
+ const currentHeight = prev.get(alertId);
2909
+ // Only update if height actually changed
2910
+ if (currentHeight !== height) {
2911
+ const newMap = new Map(prev);
2912
+ newMap.set(alertId, height);
2913
+ return newMap;
2914
+ }
2915
+ return prev;
2916
+ });
2917
+ }, []);
2918
+ // Cleanup timers on unmount
2919
+ React.useEffect(() => {
2920
+ return () => {
2921
+ timersRef.current.forEach(timer => clearTimeout(timer));
2922
+ timersRef.current.clear();
2923
+ };
2924
+ }, []);
2925
+ const contextValue = React.useMemo(() => ({ showAlert }), [showAlert]);
2926
+ // Group alerts by position
2927
+ const alertsByPosition = React.useMemo(() => {
2928
+ const grouped = {};
2929
+ alerts.forEach(alert => {
2930
+ if (!grouped[alert.position]) {
2931
+ grouped[alert.position] = [];
2932
+ }
2933
+ grouped[alert.position].push(alert);
2934
+ });
2935
+ return grouped;
2936
+ }, [alerts]);
2937
+ return (jsxRuntime.jsxs(AlertContext.Provider, { value: contextValue, children: [children, typeof window !== 'undefined' && reactDom.createPortal(jsxRuntime.jsx(jsxRuntime.Fragment, { children: Object.entries(alertsByPosition).map(([position, positionAlerts]) => {
2938
+ // positionAlerts is ordered from oldest -> newest
2939
+ const isTopPosition = position.startsWith('top');
2940
+ const ordered = isTopPosition ? [...positionAlerts].reverse() : positionAlerts;
2941
+ return ordered.map((alert, index) => {
2942
+ // Calculate offset based on actual heights of previous alerts
2943
+ let offset = 0;
2944
+ for (let i = 0; i < index; i++) {
2945
+ const prevAlert = ordered[i];
2946
+ const height = alertHeights.get(prevAlert.id) || ALERT_HEIGHT;
2947
+ offset += height + ALERT_SPACING;
2948
+ }
2949
+ return (jsxRuntime.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));
2950
+ });
2951
+ }) }), document.body)] }));
2952
+ };
2953
+ /**
2954
+ * Hook to access alert functionality
2955
+ *
2956
+ * @example
2957
+ * ```tsx
2958
+ * const { showAlert } = useAlert()
2959
+ *
2960
+ * showAlert({
2961
+ * text: 'Operation completed',
2962
+ * variant: 'success',
2963
+ * position: 'top-right'
2964
+ * })
2965
+ * ```
2966
+ */
2967
+ const useAlert = () => {
2968
+ const context = React.useContext(AlertContext);
2969
+ if (!context) {
2970
+ throw new Error('useAlert must be used within an AlertProvider');
2971
+ }
2972
+ return context;
2973
+ };
2974
+
2644
2975
  exports.Accordion = Accordion;
2976
+ exports.Alert = Alert;
2977
+ exports.AlertProvider = AlertProvider;
2645
2978
  exports.Avatar = Avatar;
2646
2979
  exports.AvatarGroup = AvatarGroup;
2647
2980
  exports.Breadcrumb = Breadcrumb;
@@ -2674,6 +3007,7 @@ exports.TabItem = TabItem;
2674
3007
  exports.Tabs = Tabs;
2675
3008
  exports.Text = Text;
2676
3009
  exports.TextArea = TextArea_default;
3010
+ exports.useAlert = useAlert;
2677
3011
  exports.useAnchorPosition = useAnchorPosition;
2678
3012
  exports.useClickOutside = useClickOutside;
2679
3013
  exports.useTransitionRender = useTransitionRender;