@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.
- package/README.md +1 -0
- package/dist/cjs/components/data-display/avatar/Avatar.props.d.ts +1 -1
- package/dist/cjs/components/forms/date-picker/calendar/Calendar.props.d.ts +2 -2
- package/dist/cjs/components/index.d.ts +2 -0
- package/dist/cjs/components/overlay/alert/Alert.d.ts +32 -0
- package/dist/cjs/components/overlay/alert/Alert.props.d.ts +56 -0
- package/dist/cjs/components/overlay/alert/Alert.styles.d.ts +4 -0
- package/dist/cjs/components/overlay/alert/index.d.ts +2 -0
- package/dist/cjs/constants/globalConstants.d.ts +1 -0
- package/dist/cjs/hooks/index.d.ts +2 -0
- package/dist/cjs/hooks/useAlert.d.ts +23 -0
- package/dist/cjs/hooks/useAlert.types.d.ts +56 -0
- package/dist/cjs/index.js +336 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/interfaces/alert.types.d.ts +8 -0
- package/dist/cjs/interfaces/index.d.ts +1 -0
- package/dist/cjs/resources/Icons.d.ts +4 -1
- package/dist/cjs/resources/icons/AlertCircleIcon.d.ts +2 -0
- package/dist/cjs/resources/icons/AlertTriangleIcon.d.ts +2 -0
- package/dist/cjs/resources/icons/CheckCircleIcon.d.ts +2 -0
- package/dist/cjs/utils/ui/components/foundation/text/getTruncateTextStyles.utils.d.ts +5 -1
- package/dist/cjs/utils/ui/components/overlay/alert/getAlertIcon.utils.d.ts +8 -0
- package/dist/cjs/utils/ui/components/overlay/alert/getAlertPositionStyles.utils.d.ts +8 -0
- package/dist/cjs/utils/ui/components/overlay/alert/getAlertVariantColors.utils.d.ts +14 -0
- package/dist/esm/components/data-display/avatar/Avatar.props.d.ts +1 -1
- package/dist/esm/components/forms/date-picker/calendar/Calendar.props.d.ts +2 -2
- package/dist/esm/components/index.d.ts +2 -0
- package/dist/esm/components/overlay/alert/Alert.d.ts +32 -0
- package/dist/esm/components/overlay/alert/Alert.props.d.ts +56 -0
- package/dist/esm/components/overlay/alert/Alert.styles.d.ts +4 -0
- package/dist/esm/components/overlay/alert/index.d.ts +2 -0
- package/dist/esm/constants/globalConstants.d.ts +1 -0
- package/dist/esm/hooks/index.d.ts +2 -0
- package/dist/esm/hooks/useAlert.d.ts +23 -0
- package/dist/esm/hooks/useAlert.types.d.ts +56 -0
- package/dist/esm/index.js +335 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/interfaces/alert.types.d.ts +8 -0
- package/dist/esm/interfaces/index.d.ts +1 -0
- package/dist/esm/resources/Icons.d.ts +4 -1
- package/dist/esm/resources/icons/AlertCircleIcon.d.ts +2 -0
- package/dist/esm/resources/icons/AlertTriangleIcon.d.ts +2 -0
- package/dist/esm/resources/icons/CheckCircleIcon.d.ts +2 -0
- package/dist/esm/utils/ui/components/foundation/text/getTruncateTextStyles.utils.d.ts +5 -1
- package/dist/esm/utils/ui/components/overlay/alert/getAlertIcon.utils.d.ts +8 -0
- package/dist/esm/utils/ui/components/overlay/alert/getAlertPositionStyles.utils.d.ts +8 -0
- package/dist/esm/utils/ui/components/overlay/alert/getAlertVariantColors.utils.d.ts +14 -0
- package/dist/index.d.ts +153 -3
- 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:
|
|
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
|
-
|
|
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
|