@aurora-ds/components 0.18.2 → 0.19.1
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/forms/select/Select.props.d.ts +11 -3
- package/dist/cjs/components/index.d.ts +3 -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 +340 -5
- 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/forms/select/Select.props.d.ts +11 -3
- package/dist/esm/components/index.d.ts +3 -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 +339 -7
- 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 +164 -6
- 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
|
};
|
|
@@ -1375,10 +1403,11 @@ MenuItem.displayName = 'MenuItem';
|
|
|
1375
1403
|
/**
|
|
1376
1404
|
* Select component that uses Menu for dropdown
|
|
1377
1405
|
*/
|
|
1378
|
-
const Select = ({ options, value, onChange, placeholder = 'Select an option', disabled = false, width }) => {
|
|
1406
|
+
const Select = ({ options, value, onChange, label, mandatory = false, placeholder = 'Select an option', disabled = false, width }) => {
|
|
1379
1407
|
const [isOpen, setIsOpen] = useState(false);
|
|
1380
1408
|
const triggerRef = useRef(null);
|
|
1381
1409
|
const selectedOption = options.find(option => option.value === value);
|
|
1410
|
+
const menuWidth = triggerRef.current?.offsetWidth ?? width;
|
|
1382
1411
|
const handleTriggerClick = () => {
|
|
1383
1412
|
if (!disabled) {
|
|
1384
1413
|
setIsOpen(!isOpen);
|
|
@@ -1388,10 +1417,10 @@ const Select = ({ options, value, onChange, placeholder = 'Select an option', di
|
|
|
1388
1417
|
setIsOpen(false);
|
|
1389
1418
|
};
|
|
1390
1419
|
const handleSelect = (selectedValue) => {
|
|
1391
|
-
onChange
|
|
1420
|
+
onChange(selectedValue);
|
|
1392
1421
|
setIsOpen(false);
|
|
1393
1422
|
};
|
|
1394
|
-
return (jsxs(
|
|
1423
|
+
return (jsxs(Stack, { direction: 'column', gap: 'xs', align: 'stretch', width: width ?? '100%', children: [label && (jsxs(Stack, { direction: 'row', gap: 'xs', align: 'center', children: [jsx(Text, { variant: 'label', fontSize: 'sm', children: label }), mandatory && (jsx(Text, { variant: 'label', fontSize: 'sm', color: 'error', children: "*" }))] })), jsxs("div", { ref: triggerRef, className: SELECT_STYLES.root({ disabled, width }), onClick: handleTriggerClick, role: 'button', tabIndex: disabled ? -1 : 0, "aria-expanded": isOpen, "aria-haspopup": 'listbox', children: [jsx("div", { className: SELECT_STYLES.trigger, children: jsx(Text, { variant: 'p', maxLines: 1, color: selectedOption ? 'text' : 'textSecondary', children: selectedOption ? selectedOption.label : placeholder }) }), jsx(Icon, { children: jsx(ChevronDownIcon, {}) })] }), jsx(Menu, { anchor: isOpen ? triggerRef.current : null, onClose: handleClose, width: menuWidth, children: jsx(MenuGroup, { children: options.map(option => (jsx(SelectItem, { option: option, isSelected: option.value === value, onSelect: handleSelect }, option.value))) }) })] }));
|
|
1395
1424
|
};
|
|
1396
1425
|
Select.displayName = 'Select';
|
|
1397
1426
|
|
|
@@ -1667,7 +1696,7 @@ const Calendar$1 = ({ value, onDateSelect, minDate, maxDate, locale = 'fr-FR', }
|
|
|
1667
1696
|
const year = value?.getFullYear() ?? new Date().getFullYear();
|
|
1668
1697
|
return Math.floor(year / 12) * 12;
|
|
1669
1698
|
});
|
|
1670
|
-
const calendarDays = useMemo(() => getCalendarDays(currentMonth.getFullYear(), currentMonth.getMonth(), value, minDate, maxDate), [currentMonth, value, minDate, maxDate]);
|
|
1699
|
+
const calendarDays = useMemo(() => getCalendarDays(currentMonth.getFullYear(), currentMonth.getMonth(), value, minDate ?? undefined, maxDate ?? undefined), [currentMonth, value, minDate, maxDate]);
|
|
1671
1700
|
const headerLabel = useMemo(() => {
|
|
1672
1701
|
if (view === 'days') {
|
|
1673
1702
|
return currentMonth.toLocaleDateString(locale, { month: 'long', year: 'numeric' });
|
|
@@ -2129,6 +2158,160 @@ const Accordion = ({ title, children, expanded, defaultExpanded = false, onChang
|
|
|
2129
2158
|
};
|
|
2130
2159
|
Accordion.displayName = 'Accordion';
|
|
2131
2160
|
|
|
2161
|
+
const ALERT_OFFSET = 16;
|
|
2162
|
+
/**
|
|
2163
|
+
* Get position styles for alert
|
|
2164
|
+
* @param position - Alert position
|
|
2165
|
+
* @param offsetY - Vertical offset for stacking (in pixels)
|
|
2166
|
+
* @returns Object with CSS position styles (top/bottom + left/right)
|
|
2167
|
+
*/
|
|
2168
|
+
const getAlertPositionStyles = (position, offsetY) => {
|
|
2169
|
+
const verticalPosition = `${ALERT_OFFSET + offsetY}px`;
|
|
2170
|
+
switch (position) {
|
|
2171
|
+
case 'top-left':
|
|
2172
|
+
return { top: verticalPosition, left: `${ALERT_OFFSET}px` };
|
|
2173
|
+
case 'top-right':
|
|
2174
|
+
return { top: verticalPosition, right: `${ALERT_OFFSET}px` };
|
|
2175
|
+
case 'bottom-left':
|
|
2176
|
+
return { bottom: verticalPosition, left: `${ALERT_OFFSET}px` };
|
|
2177
|
+
case 'bottom-right':
|
|
2178
|
+
return { bottom: verticalPosition, right: `${ALERT_OFFSET}px` };
|
|
2179
|
+
default:
|
|
2180
|
+
return { top: verticalPosition, right: `${ALERT_OFFSET}px` };
|
|
2181
|
+
}
|
|
2182
|
+
};
|
|
2183
|
+
|
|
2184
|
+
/**
|
|
2185
|
+
* Get colors for alert variants
|
|
2186
|
+
* @param theme - Aurora theme
|
|
2187
|
+
* @param variant - Alert variant
|
|
2188
|
+
* @returns Colors for the variant
|
|
2189
|
+
*/
|
|
2190
|
+
const getAlertVariantColors = (theme, variant) => {
|
|
2191
|
+
const variantColors = {
|
|
2192
|
+
default: {
|
|
2193
|
+
background: theme.colors.surface,
|
|
2194
|
+
border: theme.colors.border,
|
|
2195
|
+
iconColor: 'text'
|
|
2196
|
+
},
|
|
2197
|
+
info: {
|
|
2198
|
+
background: theme.colors.infoSubtle,
|
|
2199
|
+
border: theme.colors.info,
|
|
2200
|
+
iconColor: 'info'
|
|
2201
|
+
},
|
|
2202
|
+
warning: {
|
|
2203
|
+
background: theme.colors.warningSubtle,
|
|
2204
|
+
border: theme.colors.warning,
|
|
2205
|
+
iconColor: 'warning'
|
|
2206
|
+
},
|
|
2207
|
+
error: {
|
|
2208
|
+
background: theme.colors.errorSubtle,
|
|
2209
|
+
border: theme.colors.error,
|
|
2210
|
+
iconColor: 'error'
|
|
2211
|
+
},
|
|
2212
|
+
success: {
|
|
2213
|
+
background: theme.colors.successSubtle,
|
|
2214
|
+
border: theme.colors.success,
|
|
2215
|
+
iconColor: 'success'
|
|
2216
|
+
}
|
|
2217
|
+
};
|
|
2218
|
+
return variantColors[variant];
|
|
2219
|
+
};
|
|
2220
|
+
|
|
2221
|
+
const ALERT_STYLES = createStyles((theme) => ({
|
|
2222
|
+
root: ({ variant, position, isVisible, offsetY, maxWidth }) => {
|
|
2223
|
+
const colors = getAlertVariantColors(theme, variant);
|
|
2224
|
+
const positionStyles = getAlertPositionStyles(position, offsetY);
|
|
2225
|
+
return {
|
|
2226
|
+
position: 'fixed',
|
|
2227
|
+
...positionStyles,
|
|
2228
|
+
display: 'flex',
|
|
2229
|
+
alignItems: 'center',
|
|
2230
|
+
gap: theme.spacing.sm,
|
|
2231
|
+
padding: theme.spacing.sm,
|
|
2232
|
+
backgroundColor: colors.background,
|
|
2233
|
+
border: `1px solid ${colors.border}`,
|
|
2234
|
+
borderRadius: theme.radius.md,
|
|
2235
|
+
boxShadow: theme.shadows.lg,
|
|
2236
|
+
minWidth: ALERT_MAX_WIDTH,
|
|
2237
|
+
maxWidth,
|
|
2238
|
+
overflow: 'hidden',
|
|
2239
|
+
zIndex: theme.zIndex.toast,
|
|
2240
|
+
opacity: isVisible ? 1 : 0,
|
|
2241
|
+
transform: isVisible ? 'translateY(0)' :
|
|
2242
|
+
(position.startsWith('top') ? 'translateY(-8px)' : 'translateY(8px)'),
|
|
2243
|
+
transition: `all ${theme.transition.normal}, opacity ${theme.transition.fast}`,
|
|
2244
|
+
pointerEvents: isVisible ? 'auto' : 'none'
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
}));
|
|
2248
|
+
|
|
2249
|
+
// Pre-create icon instances to avoid recreation on every call
|
|
2250
|
+
const ALERT_ICONS = {
|
|
2251
|
+
default: jsx(InfoIcon, {}),
|
|
2252
|
+
info: jsx(InfoIcon, {}),
|
|
2253
|
+
warning: jsx(AlertTriangleIcon, {}),
|
|
2254
|
+
error: jsx(AlertCircleIcon, {}),
|
|
2255
|
+
success: jsx(CheckCircleIcon, {})
|
|
2256
|
+
};
|
|
2257
|
+
/**
|
|
2258
|
+
* Get icon component for alert variant
|
|
2259
|
+
* @param variant - Alert variant
|
|
2260
|
+
* @returns JSX Element for the icon
|
|
2261
|
+
*/
|
|
2262
|
+
const getAlertIcon = (variant) => {
|
|
2263
|
+
return ALERT_ICONS[variant];
|
|
2264
|
+
};
|
|
2265
|
+
|
|
2266
|
+
/**
|
|
2267
|
+
* Alert component - Display notifications with different variants
|
|
2268
|
+
*
|
|
2269
|
+
* **Variants:**
|
|
2270
|
+
* - `default`: Standard alert with neutral styling
|
|
2271
|
+
* - `info`: Informational alert with blue styling
|
|
2272
|
+
* - `warning`: Warning alert with yellow/orange styling
|
|
2273
|
+
* - `error`: Error alert with red styling
|
|
2274
|
+
* - `success`: Success alert with green styling
|
|
2275
|
+
*
|
|
2276
|
+
* **Features:**
|
|
2277
|
+
* - Auto-dismisses after 3 seconds
|
|
2278
|
+
* - Positioned at screen corners
|
|
2279
|
+
* - Supports stacking with offsetY
|
|
2280
|
+
* - Icon based on variant
|
|
2281
|
+
* - Smooth animations
|
|
2282
|
+
* - Dynamic height calculation for proper stacking
|
|
2283
|
+
*
|
|
2284
|
+
* @example
|
|
2285
|
+
* ```tsx
|
|
2286
|
+
* <Alert
|
|
2287
|
+
* text="Operation completed successfully"
|
|
2288
|
+
* variant="success"
|
|
2289
|
+
* position="top-right"
|
|
2290
|
+
* isVisible={true}
|
|
2291
|
+
* />
|
|
2292
|
+
* ```
|
|
2293
|
+
*/
|
|
2294
|
+
const Alert = memo(({ text, variant = 'default', position = 'top-right', isVisible = false, offsetY = 0, maxWidth = ALERT_MAX_WIDTH, alertId, onHeightChange }) => {
|
|
2295
|
+
const theme = useTheme();
|
|
2296
|
+
const alertRef = useRef(null);
|
|
2297
|
+
const lastHeightRef = useRef(0);
|
|
2298
|
+
const icon = useMemo(() => getAlertIcon(variant), [variant]);
|
|
2299
|
+
const colors = useMemo(() => getAlertVariantColors(theme, variant), [theme, variant]);
|
|
2300
|
+
// Report height changes to parent (only when height actually changes)
|
|
2301
|
+
useEffect(() => {
|
|
2302
|
+
if (alertRef.current && onHeightChange && isVisible && alertId) {
|
|
2303
|
+
const height = alertRef.current.offsetHeight;
|
|
2304
|
+
// Only call onHeightChange if height has actually changed
|
|
2305
|
+
if (height !== lastHeightRef.current) {
|
|
2306
|
+
lastHeightRef.current = height;
|
|
2307
|
+
onHeightChange(alertId, height);
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
}, [isVisible, text, maxWidth, alertId, onHeightChange]);
|
|
2311
|
+
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 })] }));
|
|
2312
|
+
});
|
|
2313
|
+
Alert.displayName = 'Alert';
|
|
2314
|
+
|
|
2132
2315
|
const MODAL_STYLES = createStyles((theme) => ({
|
|
2133
2316
|
background: (isFadingIn) => ({
|
|
2134
2317
|
background: 'rgba(0, 0, 0, 0.6)',
|
|
@@ -2639,5 +2822,154 @@ const Pagination = ({ currentPage, totalPages, onPageChange, onPrevious, onNext,
|
|
|
2639
2822
|
};
|
|
2640
2823
|
Pagination.displayName = 'Pagination';
|
|
2641
2824
|
|
|
2642
|
-
|
|
2825
|
+
const AlertContext = createContext(undefined);
|
|
2826
|
+
const ALERT_DURATION = 3000;
|
|
2827
|
+
const MAX_ALERTS = 2;
|
|
2828
|
+
const ALERT_HEIGHT = 48; // Approximate height with padding (px)
|
|
2829
|
+
const ALERT_SPACING = 6; // Spacing between alerts (px)
|
|
2830
|
+
/**
|
|
2831
|
+
* Alert Provider Component
|
|
2832
|
+
* Manages alert queue and renders alerts in a portal
|
|
2833
|
+
*/
|
|
2834
|
+
const AlertProvider = ({ children }) => {
|
|
2835
|
+
const [alerts, setAlerts] = useState([]);
|
|
2836
|
+
const [visibleAlerts, setVisibleAlerts] = useState(new Set());
|
|
2837
|
+
const [alertHeights, setAlertHeights] = useState(new Map());
|
|
2838
|
+
const timersRef = useRef(new Map());
|
|
2839
|
+
const showAlert = useCallback((options) => {
|
|
2840
|
+
const { text, variant = 'default', position = 'top-right', duration = ALERT_DURATION, maxWidth = 300 } = options;
|
|
2841
|
+
const id = `alert-${Date.now()}-${Math.random()}`;
|
|
2842
|
+
const newAlert = {
|
|
2843
|
+
id,
|
|
2844
|
+
text,
|
|
2845
|
+
variant,
|
|
2846
|
+
position,
|
|
2847
|
+
timestamp: Date.now(),
|
|
2848
|
+
maxWidth
|
|
2849
|
+
};
|
|
2850
|
+
setAlerts((prev) => {
|
|
2851
|
+
const updatedAlerts = [...prev, newAlert];
|
|
2852
|
+
// Keep only MAX_ALERTS for the same position
|
|
2853
|
+
const samePositionAlerts = updatedAlerts.filter(a => a.position === position);
|
|
2854
|
+
if (samePositionAlerts.length > MAX_ALERTS) {
|
|
2855
|
+
// Remove the oldest alert for this position
|
|
2856
|
+
const oldestAlert = samePositionAlerts[0];
|
|
2857
|
+
const timerToRemove = timersRef.current.get(oldestAlert.id);
|
|
2858
|
+
if (timerToRemove) {
|
|
2859
|
+
clearTimeout(timerToRemove);
|
|
2860
|
+
timersRef.current.delete(oldestAlert.id);
|
|
2861
|
+
}
|
|
2862
|
+
setVisibleAlerts((prevVisible) => {
|
|
2863
|
+
const newVisible = new Set(prevVisible);
|
|
2864
|
+
newVisible.delete(oldestAlert.id);
|
|
2865
|
+
return newVisible;
|
|
2866
|
+
});
|
|
2867
|
+
// Remove from alerts array after animation
|
|
2868
|
+
setTimeout(() => {
|
|
2869
|
+
setAlerts((current) => current.filter(a => a.id !== oldestAlert.id));
|
|
2870
|
+
setAlertHeights((prevHeights) => {
|
|
2871
|
+
const newMap = new Map(prevHeights);
|
|
2872
|
+
newMap.delete(oldestAlert.id);
|
|
2873
|
+
return newMap;
|
|
2874
|
+
});
|
|
2875
|
+
}, 300);
|
|
2876
|
+
return updatedAlerts.filter(a => a.id !== oldestAlert.id);
|
|
2877
|
+
}
|
|
2878
|
+
return updatedAlerts;
|
|
2879
|
+
});
|
|
2880
|
+
// Show alert after a small delay for animation
|
|
2881
|
+
setTimeout(() => {
|
|
2882
|
+
setVisibleAlerts((prev) => new Set(prev).add(id));
|
|
2883
|
+
}, 10);
|
|
2884
|
+
// Auto-dismiss timer
|
|
2885
|
+
const timer = setTimeout(() => {
|
|
2886
|
+
setVisibleAlerts((prev) => {
|
|
2887
|
+
const newVisible = new Set(prev);
|
|
2888
|
+
newVisible.delete(id);
|
|
2889
|
+
return newVisible;
|
|
2890
|
+
});
|
|
2891
|
+
// Remove from DOM after animation
|
|
2892
|
+
setTimeout(() => {
|
|
2893
|
+
setAlerts((prev) => prev.filter(a => a.id !== id));
|
|
2894
|
+
setAlertHeights((prev) => {
|
|
2895
|
+
const newMap = new Map(prev);
|
|
2896
|
+
newMap.delete(id);
|
|
2897
|
+
return newMap;
|
|
2898
|
+
});
|
|
2899
|
+
timersRef.current.delete(id);
|
|
2900
|
+
}, 300);
|
|
2901
|
+
}, duration);
|
|
2902
|
+
timersRef.current.set(id, timer);
|
|
2903
|
+
}, []);
|
|
2904
|
+
// Handle height changes from alerts (memoized to avoid re-creating on every render)
|
|
2905
|
+
const handleHeightChange = useCallback((alertId, height) => {
|
|
2906
|
+
setAlertHeights((prev) => {
|
|
2907
|
+
const currentHeight = prev.get(alertId);
|
|
2908
|
+
// Only update if height actually changed
|
|
2909
|
+
if (currentHeight !== height) {
|
|
2910
|
+
const newMap = new Map(prev);
|
|
2911
|
+
newMap.set(alertId, height);
|
|
2912
|
+
return newMap;
|
|
2913
|
+
}
|
|
2914
|
+
return prev;
|
|
2915
|
+
});
|
|
2916
|
+
}, []);
|
|
2917
|
+
// Cleanup timers on unmount
|
|
2918
|
+
useEffect(() => {
|
|
2919
|
+
return () => {
|
|
2920
|
+
timersRef.current.forEach(timer => clearTimeout(timer));
|
|
2921
|
+
timersRef.current.clear();
|
|
2922
|
+
};
|
|
2923
|
+
}, []);
|
|
2924
|
+
const contextValue = useMemo(() => ({ showAlert }), [showAlert]);
|
|
2925
|
+
// Group alerts by position
|
|
2926
|
+
const alertsByPosition = useMemo(() => {
|
|
2927
|
+
const grouped = {};
|
|
2928
|
+
alerts.forEach(alert => {
|
|
2929
|
+
if (!grouped[alert.position]) {
|
|
2930
|
+
grouped[alert.position] = [];
|
|
2931
|
+
}
|
|
2932
|
+
grouped[alert.position].push(alert);
|
|
2933
|
+
});
|
|
2934
|
+
return grouped;
|
|
2935
|
+
}, [alerts]);
|
|
2936
|
+
return (jsxs(AlertContext.Provider, { value: contextValue, children: [children, typeof window !== 'undefined' && createPortal(jsx(Fragment$1, { children: Object.entries(alertsByPosition).map(([position, positionAlerts]) => {
|
|
2937
|
+
// positionAlerts is ordered from oldest -> newest
|
|
2938
|
+
const isTopPosition = position.startsWith('top');
|
|
2939
|
+
const ordered = isTopPosition ? [...positionAlerts].reverse() : positionAlerts;
|
|
2940
|
+
return ordered.map((alert, index) => {
|
|
2941
|
+
// Calculate offset based on actual heights of previous alerts
|
|
2942
|
+
let offset = 0;
|
|
2943
|
+
for (let i = 0; i < index; i++) {
|
|
2944
|
+
const prevAlert = ordered[i];
|
|
2945
|
+
const height = alertHeights.get(prevAlert.id) || ALERT_HEIGHT;
|
|
2946
|
+
offset += height + ALERT_SPACING;
|
|
2947
|
+
}
|
|
2948
|
+
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));
|
|
2949
|
+
});
|
|
2950
|
+
}) }), document.body)] }));
|
|
2951
|
+
};
|
|
2952
|
+
/**
|
|
2953
|
+
* Hook to access alert functionality
|
|
2954
|
+
*
|
|
2955
|
+
* @example
|
|
2956
|
+
* ```tsx
|
|
2957
|
+
* const { showAlert } = useAlert()
|
|
2958
|
+
*
|
|
2959
|
+
* showAlert({
|
|
2960
|
+
* text: 'Operation completed',
|
|
2961
|
+
* variant: 'success',
|
|
2962
|
+
* position: 'top-right'
|
|
2963
|
+
* })
|
|
2964
|
+
* ```
|
|
2965
|
+
*/
|
|
2966
|
+
const useAlert = () => {
|
|
2967
|
+
const context = useContext(AlertContext);
|
|
2968
|
+
if (!context) {
|
|
2969
|
+
throw new Error('useAlert must be used within an AlertProvider');
|
|
2970
|
+
}
|
|
2971
|
+
return context;
|
|
2972
|
+
};
|
|
2973
|
+
|
|
2974
|
+
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
2975
|
//# sourceMappingURL=index.js.map
|