@aurora-ds/components 0.10.0 → 0.14.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 (81) hide show
  1. package/README.md +16 -5
  2. package/dist/cjs/components/actions/button/Button.props.d.ts +2 -2
  3. package/dist/cjs/components/actions/icon-button/IconButton.props.d.ts +2 -2
  4. package/dist/cjs/components/data-display/avatar/Avatar.d.ts +9 -0
  5. package/dist/cjs/components/data-display/avatar/Avatar.props.d.ts +25 -0
  6. package/dist/cjs/components/data-display/avatar/Avatar.styles.d.ts +10 -0
  7. package/dist/cjs/components/data-display/avatar/AvatarGroup.d.ts +9 -0
  8. package/dist/cjs/components/data-display/avatar/avatar-group/AvatarGroup.d.ts +9 -0
  9. package/dist/cjs/components/data-display/avatar/avatar-group/AvatarGroup.props.d.ts +10 -0
  10. package/dist/cjs/components/data-display/avatar/avatar-group/AvatarGroup.styles.d.ts +14 -0
  11. package/dist/cjs/components/data-display/avatar/index.d.ts +4 -0
  12. package/dist/cjs/components/index.d.ts +4 -0
  13. package/dist/cjs/components/layout/separator/Separator.d.ts +9 -0
  14. package/dist/cjs/components/layout/separator/Separator.props.d.ts +17 -0
  15. package/dist/cjs/components/layout/separator/Separator.styles.d.ts +7 -0
  16. package/dist/cjs/components/layout/separator/index.d.ts +2 -0
  17. package/dist/cjs/components/navigation/tabs/Tabs.d.ts +7 -0
  18. package/dist/cjs/components/navigation/tabs/Tabs.props.d.ts +12 -0
  19. package/dist/cjs/components/navigation/tabs/Tabs.styles.d.ts +7 -0
  20. package/dist/cjs/components/navigation/tabs/index.d.ts +4 -0
  21. package/dist/cjs/components/navigation/tabs/tab-item/TabItem.d.ts +7 -0
  22. package/dist/cjs/components/navigation/tabs/tab-item/TabItem.props.d.ts +10 -0
  23. package/dist/cjs/components/navigation/tabs/tab-item/TabItem.styles.d.ts +6 -0
  24. package/dist/cjs/components/overlay/menu/Menu.d.ts +4 -0
  25. package/dist/cjs/components/overlay/menu/Menu.props.d.ts +8 -0
  26. package/dist/cjs/components/overlay/menu/Menu.styles.d.ts +4 -0
  27. package/dist/cjs/components/overlay/menu/index.d.ts +2 -0
  28. package/dist/cjs/constants/globalConstants.d.ts +1 -0
  29. package/dist/cjs/hooks/index.d.ts +5 -0
  30. package/dist/cjs/hooks/useAnchorPosition.d.ts +17 -0
  31. package/dist/cjs/hooks/useAnchorPosition.types.d.ts +5 -0
  32. package/dist/cjs/hooks/useClickOutside.d.ts +8 -0
  33. package/dist/cjs/hooks/useTransitionRender.d.ts +7 -0
  34. package/dist/cjs/hooks/useTransitionRender.types.d.ts +4 -0
  35. package/dist/cjs/index.d.ts +1 -0
  36. package/dist/cjs/index.js +381 -33
  37. package/dist/cjs/index.js.map +1 -1
  38. package/dist/cjs/interfaces/avatar.types.d.ts +2 -0
  39. package/dist/cjs/interfaces/index.d.ts +1 -0
  40. package/dist/cjs/utils/ui/components/data-display/avatar/getAvatarSizes.utils.d.ts +11 -0
  41. package/dist/esm/components/actions/button/Button.props.d.ts +2 -2
  42. package/dist/esm/components/actions/icon-button/IconButton.props.d.ts +2 -2
  43. package/dist/esm/components/data-display/avatar/Avatar.d.ts +9 -0
  44. package/dist/esm/components/data-display/avatar/Avatar.props.d.ts +25 -0
  45. package/dist/esm/components/data-display/avatar/Avatar.styles.d.ts +10 -0
  46. package/dist/esm/components/data-display/avatar/AvatarGroup.d.ts +9 -0
  47. package/dist/esm/components/data-display/avatar/avatar-group/AvatarGroup.d.ts +9 -0
  48. package/dist/esm/components/data-display/avatar/avatar-group/AvatarGroup.props.d.ts +10 -0
  49. package/dist/esm/components/data-display/avatar/avatar-group/AvatarGroup.styles.d.ts +14 -0
  50. package/dist/esm/components/data-display/avatar/index.d.ts +4 -0
  51. package/dist/esm/components/index.d.ts +4 -0
  52. package/dist/esm/components/layout/separator/Separator.d.ts +9 -0
  53. package/dist/esm/components/layout/separator/Separator.props.d.ts +17 -0
  54. package/dist/esm/components/layout/separator/Separator.styles.d.ts +7 -0
  55. package/dist/esm/components/layout/separator/index.d.ts +2 -0
  56. package/dist/esm/components/navigation/tabs/Tabs.d.ts +7 -0
  57. package/dist/esm/components/navigation/tabs/Tabs.props.d.ts +12 -0
  58. package/dist/esm/components/navigation/tabs/Tabs.styles.d.ts +7 -0
  59. package/dist/esm/components/navigation/tabs/index.d.ts +4 -0
  60. package/dist/esm/components/navigation/tabs/tab-item/TabItem.d.ts +7 -0
  61. package/dist/esm/components/navigation/tabs/tab-item/TabItem.props.d.ts +10 -0
  62. package/dist/esm/components/navigation/tabs/tab-item/TabItem.styles.d.ts +6 -0
  63. package/dist/esm/components/overlay/menu/Menu.d.ts +4 -0
  64. package/dist/esm/components/overlay/menu/Menu.props.d.ts +8 -0
  65. package/dist/esm/components/overlay/menu/Menu.styles.d.ts +4 -0
  66. package/dist/esm/components/overlay/menu/index.d.ts +2 -0
  67. package/dist/esm/constants/globalConstants.d.ts +1 -0
  68. package/dist/esm/hooks/index.d.ts +5 -0
  69. package/dist/esm/hooks/useAnchorPosition.d.ts +17 -0
  70. package/dist/esm/hooks/useAnchorPosition.types.d.ts +5 -0
  71. package/dist/esm/hooks/useClickOutside.d.ts +8 -0
  72. package/dist/esm/hooks/useTransitionRender.d.ts +7 -0
  73. package/dist/esm/hooks/useTransitionRender.types.d.ts +4 -0
  74. package/dist/esm/index.d.ts +1 -0
  75. package/dist/esm/index.js +341 -2
  76. package/dist/esm/index.js.map +1 -1
  77. package/dist/esm/interfaces/avatar.types.d.ts +2 -0
  78. package/dist/esm/interfaces/index.d.ts +1 -0
  79. package/dist/esm/utils/ui/components/data-display/avatar/getAvatarSizes.utils.d.ts +11 -0
  80. package/dist/index.d.ts +146 -5
  81. package/package.json +1 -1
package/dist/cjs/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
- var react = require('react');
4
+ var React = require('react');
5
5
  var theme = require('@aurora-ds/theme');
6
6
 
7
7
  const ICON_STYLES = theme.createStyles((theme) => ({
@@ -58,9 +58,9 @@ const ICON_STYLES = theme.createStyles((theme) => ({
58
58
  */
59
59
  const Icon = ({ children, size, color, backgroundColor, padding, borderRadius, ariaLabel, ariaLabelledBy, ariaDescribedBy, role, tabIndex, }) => {
60
60
  // Clone child element to apply width and height
61
- const child = react.Children.only(children);
62
- const styledChild = react.isValidElement(child)
63
- ? react.cloneElement(child, {
61
+ const child = React.Children.only(children);
62
+ const styledChild = React.isValidElement(child)
63
+ ? React.cloneElement(child, {
64
64
  width: '100%',
65
65
  height: '100%',
66
66
  })
@@ -183,7 +183,7 @@ const parseTextWithBold = (children) => {
183
183
  if (match.index > lastIndex) {
184
184
  parts.push(children.slice(lastIndex, match.index));
185
185
  }
186
- parts.push(react.createElement('strong', { key: match.index }, match[1]));
186
+ parts.push(React.createElement('strong', { key: match.index }, match[1]));
187
187
  lastIndex = match.index + match[0].length;
188
188
  }
189
189
  if (parts.length === 0) {
@@ -192,7 +192,7 @@ const parseTextWithBold = (children) => {
192
192
  if (lastIndex < children.length) {
193
193
  parts.push(children.slice(lastIndex));
194
194
  }
195
- return react.createElement(react.Fragment, null, ...parts);
195
+ return React.createElement(React.Fragment, null, ...parts);
196
196
  };
197
197
 
198
198
  /**
@@ -214,10 +214,10 @@ const parseTextWithBold = (children) => {
214
214
  */
215
215
  const Text = ({ children, variant = 'span', color, fontSize, fontFamily, maxLines, underline, preserveWhitespace, ariaLabel, ariaLabelledBy, ariaDescribedBy, role, tabIndex, }) => {
216
216
  const theme$1 = theme.useTheme();
217
- const variantStyles = react.useMemo(() => getTextVariantStyles(theme$1), [theme$1]);
217
+ const variantStyles = React.useMemo(() => getTextVariantStyles(theme$1), [theme$1]);
218
218
  const tag = variantStyles[variant].tag;
219
- const parsedChildren = react.useMemo(() => parseTextWithBold(children), [children]);
220
- return react.createElement(tag, {
219
+ const parsedChildren = React.useMemo(() => parseTextWithBold(children), [children]);
220
+ return React.createElement(tag, {
221
221
  className: TEXT_STYLES.root({ variant, color, fontSize, fontFamily, maxLines, underline, preserveWhitespace }),
222
222
  'aria-label': ariaLabel,
223
223
  'aria-labelledby': ariaLabelledBy,
@@ -420,6 +420,119 @@ Chip.displayName = 'Chip';
420
420
 
421
421
  const BUTTON_SIZE = 36;
422
422
  const DRAWER_ITEM_HEIGHT = 32;
423
+ const DEFAULT_TRANSITION_DURATION_MS = 150;
424
+
425
+ /**
426
+ * Get avatar sizes configuration based on the theme
427
+ * @param theme
428
+ */
429
+ const getAvatarSizes = (theme) => ({
430
+ small: { size: BUTTON_SIZE - 8, fontSize: 'xs', overlap: `-${theme.spacing.md}` },
431
+ medium: { size: BUTTON_SIZE, fontSize: 'sm', overlap: `-${theme.spacing.md}` },
432
+ large: { size: BUTTON_SIZE + 8, fontSize: 'sm', overlap: `-${theme.spacing.lg}` },
433
+ });
434
+
435
+ /**
436
+ * Avatar styles using createStyles from @aurora-ds/theme
437
+ */
438
+ const AVATAR_STYLES = theme.createStyles((theme) => {
439
+ const AVATAR_SIZES = getAvatarSizes(theme);
440
+ return {
441
+ root: ({ hasImage = false, clickable = false, size = 'medium', color, borderColor, backgroundColor }) => {
442
+ const sizeConfig = AVATAR_SIZES[size];
443
+ return {
444
+ width: sizeConfig.size,
445
+ height: sizeConfig.size,
446
+ borderRadius: theme.radius.full,
447
+ backgroundColor: backgroundColor || (hasImage ? 'transparent' : theme.colors.surfaceHover),
448
+ display: 'flex',
449
+ alignItems: 'center',
450
+ justifyContent: 'center',
451
+ cursor: clickable ? 'pointer' : 'default',
452
+ border: hasImage && !borderColor ? 'none' : `1px solid ${borderColor || theme.colors.border}`,
453
+ overflow: 'hidden',
454
+ flexShrink: 0,
455
+ color: color || theme.colors.textSecondary,
456
+ fontWeight: theme.fontWeight.medium,
457
+ };
458
+ },
459
+ image: {
460
+ width: '100%',
461
+ height: '100%',
462
+ objectFit: 'cover',
463
+ },
464
+ };
465
+ });
466
+
467
+ /**
468
+ * Avatar component
469
+ *
470
+ * Displays a user's avatar with optional image or fallback text.
471
+ */
472
+ const Avatar = ({ image, label, onClick, size = 'medium', color, borderColor, backgroundColor, }) => {
473
+ // hooks
474
+ const theme$1 = theme.useTheme();
475
+ // variables
476
+ const AVATAR_SIZES = getAvatarSizes(theme$1);
477
+ const hasImage = !!image;
478
+ const clickable = !!onClick;
479
+ 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 || '?' })) }));
480
+ };
481
+ Avatar.displayName = 'Avatar';
482
+
483
+ /**
484
+ * AvatarGroup styles using createStyles from @aurora-ds/theme
485
+ */
486
+ const AVATAR_GROUP_STYLES = theme.createStyles((theme) => {
487
+ const AVATAR_SIZES = getAvatarSizes(theme);
488
+ return {
489
+ root: {
490
+ display: 'flex',
491
+ alignItems: 'center',
492
+ },
493
+ child: ({ size = 'medium' }) => ({
494
+ marginLeft: AVATAR_SIZES[size].overlap,
495
+ }),
496
+ firstChild: {
497
+ marginLeft: '0',
498
+ },
499
+ more: ({ size = 'medium' }) => {
500
+ const sizeConfig = AVATAR_SIZES[size];
501
+ return {
502
+ width: sizeConfig.size,
503
+ height: sizeConfig.size,
504
+ borderRadius: theme.radius.full,
505
+ backgroundColor: theme.colors.surfaceHover,
506
+ border: `1px solid ${theme.colors.border}`,
507
+ display: 'flex',
508
+ alignItems: 'center',
509
+ justifyContent: 'center',
510
+ color: theme.colors.textSecondary,
511
+ fontSize: sizeConfig.fontSize,
512
+ fontWeight: theme.fontWeight.medium,
513
+ flexShrink: 0,
514
+ marginLeft: AVATAR_SIZES[size].overlap,
515
+ };
516
+ },
517
+ };
518
+ });
519
+
520
+ /**
521
+ * AvatarGroup component
522
+ *
523
+ * Groups multiple avatars with overlapping display and optional limit.
524
+ */
525
+ const AvatarGroup = ({ children, limit, size = 'medium' }) => {
526
+ // hooks
527
+ const theme$1 = theme.useTheme();
528
+ // variables
529
+ const AVATAR_SIZES = getAvatarSizes(theme$1);
530
+ const childArray = React.Children.toArray(children);
531
+ const displayedChildren = limit ? childArray.slice(0, limit) : childArray;
532
+ const moreCount = limit && childArray.length > limit ? childArray.length - limit : 0;
533
+ return (jsxRuntime.jsxs("div", { className: AVATAR_GROUP_STYLES.root, children: [displayedChildren.map((child, index) => (jsxRuntime.jsx("div", { className: index === 0 ? AVATAR_GROUP_STYLES.firstChild : AVATAR_GROUP_STYLES.child({ size }), children: child }, index))), moreCount > 0 && (jsxRuntime.jsx("div", { className: AVATAR_GROUP_STYLES.more({ size }), children: jsxRuntime.jsxs(Text, { variant: 'label', fontSize: AVATAR_SIZES[size].fontSize, children: ["+", moreCount] }) }))] }));
534
+ };
535
+ AvatarGroup.displayName = 'AvatarGroup';
423
536
 
424
537
  const getButtonSizeStyles = () => ({
425
538
  small: {
@@ -653,14 +766,14 @@ const FORM_STYLES = theme.createStyles(() => ({
653
766
  * Form component - A transparent wrapper for form elements with automatic preventDefault handling
654
767
  */
655
768
  const Form = ({ children, onSubmit, ariaLabel, }) => {
656
- const handleSubmit = react.useCallback((e) => {
769
+ const handleSubmit = React.useCallback((e) => {
657
770
  e.preventDefault();
658
771
  onSubmit?.(e);
659
772
  }, [onSubmit]);
660
773
  return (jsxRuntime.jsx("form", { onSubmit: handleSubmit, className: FORM_STYLES.root, "aria-label": ariaLabel, children: children }));
661
774
  };
662
775
  Form.displayName = 'Form';
663
- var Form_default = react.memo(Form);
776
+ var Form_default = React.memo(Form);
664
777
 
665
778
  /**
666
779
  * Input styles using createStyles from @aurora-ds/theme
@@ -800,8 +913,8 @@ const MoreHorizontalIcon = () => {
800
913
  /**
801
914
  * Input component
802
915
  */
803
- const Input = react.forwardRef(({ value, onChange, label, mandatory = false, placeholder, disabled = false, type = 'text', ariaLabel, startIcon, endIcon, width, }, ref) => {
804
- const [showPassword, setShowPassword] = react.useState(false);
916
+ const Input = React.forwardRef(({ value, onChange, label, mandatory = false, placeholder, disabled = false, type = 'text', ariaLabel, startIcon, endIcon, width, }, ref) => {
917
+ const [showPassword, setShowPassword] = React.useState(false);
805
918
  const handleChange = (event) => {
806
919
  onChange(event.target.value);
807
920
  };
@@ -810,7 +923,7 @@ const Input = react.forwardRef(({ value, onChange, label, mandatory = false, pla
810
923
  return (jsxRuntime.jsxs(Stack, { direction: 'column', gap: 'xs', align: 'stretch', width: width ?? '100%', children: [label && (jsxRuntime.jsxs(Stack, { direction: 'row', gap: 'xs', align: 'center', children: [jsxRuntime.jsx(Text, { variant: 'label', fontSize: 'sm', children: label }), mandatory && (jsxRuntime.jsx(Text, { variant: 'label', fontSize: 'sm', color: 'error', children: "*" }))] })), jsxRuntime.jsxs("div", { className: `${INPUT_STYLES.container({ width })} ${disabled ? 'disabled' : ''}`, children: [jsxRuntime.jsx("input", { ref: ref, type: inputType, value: value, onChange: handleChange, placeholder: placeholder, disabled: disabled, className: INPUT_STYLES.root({ disabled, hasStartIcon: !!startIcon, hasEndIcon: !!endIcon, hasPasswordToggle }), "aria-label": ariaLabel || label }), startIcon && (jsxRuntime.jsx("div", { className: INPUT_STYLES.startIcon, children: jsxRuntime.jsx(Icon, { color: 'textTertiary', children: startIcon }) })), endIcon && (jsxRuntime.jsx("div", { className: hasPasswordToggle ? INPUT_STYLES.endIconShifted : INPUT_STYLES.endIcon, children: jsxRuntime.jsx(Icon, { color: 'textTertiary', children: endIcon }) })), hasPasswordToggle && (jsxRuntime.jsx("div", { className: INPUT_STYLES.passwordToggle, children: jsxRuntime.jsx(IconButton, { icon: showPassword ? jsxRuntime.jsx(EyeOffIcon, {}) : jsxRuntime.jsx(EyeIcon, {}), onClick: () => setShowPassword(!showPassword), disabled: disabled, ariaLabel: ariaLabel || label, variant: 'text', size: 'small', textColor: 'textSecondary' }) }))] })] }));
811
924
  });
812
925
  Input.displayName = 'Input';
813
- var Input_default = react.memo(Input);
926
+ var Input_default = React.memo(Input);
814
927
 
815
928
  /**
816
929
  * TextArea styles using createStyles from @aurora-ds/theme
@@ -860,10 +973,10 @@ const TEXTAREA_STYLES = theme.createStyles((theme) => ({
860
973
  /**
861
974
  * TextArea component with auto-expanding height based on content
862
975
  */
863
- const TextArea = react.forwardRef(({ value, onChange, label, mandatory = false, placeholder, disabled = false, ariaLabel, width, minRows = 3, maxRows, }, ref) => {
864
- const internalRef = react.useRef(null);
976
+ const TextArea = React.forwardRef(({ value, onChange, label, mandatory = false, placeholder, disabled = false, ariaLabel, width, minRows = 3, maxRows, }, ref) => {
977
+ const internalRef = React.useRef(null);
865
978
  const textareaRef = ref || internalRef;
866
- const adjustHeight = react.useCallback(() => {
979
+ const adjustHeight = React.useCallback(() => {
867
980
  const textarea = textareaRef.current;
868
981
  if (textarea) {
869
982
  // Reset height to calculate the correct scrollHeight
@@ -879,7 +992,7 @@ const TextArea = react.forwardRef(({ value, onChange, label, mandatory = false,
879
992
  textarea.style.overflowY = textarea.scrollHeight > maxHeight ? 'auto' : 'hidden';
880
993
  }
881
994
  }, [minRows, maxRows, textareaRef]);
882
- react.useEffect(() => {
995
+ React.useEffect(() => {
883
996
  adjustHeight();
884
997
  }, [value, adjustHeight]);
885
998
  const handleChange = (event) => {
@@ -888,7 +1001,7 @@ const TextArea = react.forwardRef(({ value, onChange, label, mandatory = false,
888
1001
  return (jsxRuntime.jsxs(Stack, { direction: 'column', gap: 'xs', align: 'stretch', width: width ?? '100%', children: [label && (jsxRuntime.jsxs(Stack, { direction: 'row', gap: 'xs', align: 'center', children: [jsxRuntime.jsx(Text, { variant: 'label', fontSize: 'sm', children: label }), mandatory && (jsxRuntime.jsx(Text, { variant: 'label', fontSize: 'sm', color: 'error', children: "*" }))] })), jsxRuntime.jsx("div", { className: `${TEXTAREA_STYLES.container({ width })} ${disabled ? 'disabled' : ''}`, children: jsxRuntime.jsx("textarea", { ref: textareaRef, value: value, onChange: handleChange, placeholder: placeholder, disabled: disabled, className: TEXTAREA_STYLES.root({ disabled }), "aria-label": ariaLabel || label, rows: minRows }) })] }));
889
1002
  });
890
1003
  TextArea.displayName = 'TextArea';
891
- var TextArea_default = react.memo(TextArea);
1004
+ var TextArea_default = React.memo(TextArea);
892
1005
 
893
1006
  /**
894
1007
  * Card styles using createStyles from @aurora-ds/theme
@@ -1009,6 +1122,34 @@ const Grid = ({ children, columns = 1, rows, gap = 'sm', columnGap, rowGap, widt
1009
1122
  };
1010
1123
  Grid.displayName = 'Grid';
1011
1124
 
1125
+ /**
1126
+ * Separator styles using createStyles from @aurora-ds/theme
1127
+ */
1128
+ const SEPARATOR_STYLES = theme.createStyles((theme) => ({
1129
+ root: ({ direction = 'horizontal', color = 'border', width, height }) => ({
1130
+ backgroundColor: theme.colors[color],
1131
+ ...(direction === 'horizontal' ? {
1132
+ width: '100%',
1133
+ height: '1px',
1134
+ } : {
1135
+ width: '1px',
1136
+ height: '100%',
1137
+ }),
1138
+ ...(width !== undefined && { width }),
1139
+ ...(height !== undefined && { height }),
1140
+ }),
1141
+ }));
1142
+
1143
+ /**
1144
+ * Separator component
1145
+ *
1146
+ * A simple separator line for horizontal or vertical division.
1147
+ */
1148
+ const Separator = ({ direction = 'horizontal', width, height, color = 'border', }) => {
1149
+ return (jsxRuntime.jsx("div", { className: SEPARATOR_STYLES.root({ direction, color, width, height }) }));
1150
+ };
1151
+ Separator.displayName = 'Separator';
1152
+
1012
1153
  const PAGE_SECTION_STYLES = theme.createStyles((theme) => ({
1013
1154
  root: ({ gap, paddingHorizontal, paddingVertical, alignItems = 'center', maxWidth, minHeight }) => ({
1014
1155
  display: 'flex',
@@ -1106,7 +1247,7 @@ const ACCORDION_STYLES = theme.createStyles((theme) => {
1106
1247
  * Supports controlled and uncontrolled modes.
1107
1248
  */
1108
1249
  const Accordion = ({ title, children, expanded, defaultExpanded = false, onChange, disabled = false, icon, backgroundColor, width, headerPadding, contentPadding, ariaLabel, ariaLabelledBy, ariaDescribedBy, role, tabIndex, }) => {
1109
- const [internalExpanded, setInternalExpanded] = react.useState(defaultExpanded);
1250
+ const [internalExpanded, setInternalExpanded] = React.useState(defaultExpanded);
1110
1251
  // Use controlled value if provided, otherwise use internal state
1111
1252
  const isExpanded = expanded !== undefined ? expanded : internalExpanded;
1112
1253
  const handleToggle = () => {
@@ -1123,6 +1264,141 @@ const Accordion = ({ title, children, expanded, defaultExpanded = false, onChang
1123
1264
  };
1124
1265
  Accordion.displayName = 'Accordion';
1125
1266
 
1267
+ const MENU_STYLES = theme.createStyles((theme) => ({
1268
+ root: (isFadingIn, anchorOrigin, position) => ({
1269
+ position: 'absolute',
1270
+ zIndex: theme.zIndex.dropdown,
1271
+ marginTop: theme.spacing.xs,
1272
+ backgroundColor: theme.colors.surface,
1273
+ borderRadius: theme.radius.md,
1274
+ border: `1px solid ${theme.colors.border}`,
1275
+ boxShadow: theme.shadows.md,
1276
+ minWidth: 150,
1277
+ opacity: isFadingIn ? 1 : 0,
1278
+ transform: isFadingIn ? 'scale(1)' : 'scale(0.95)',
1279
+ transformOrigin: anchorOrigin === 'right' ? 'top right' : 'top left',
1280
+ transition: `opacity ${DEFAULT_TRANSITION_DURATION_MS}ms ease-out, transform ${DEFAULT_TRANSITION_DURATION_MS}ms ease-out`,
1281
+ top: position?.top ?? 0,
1282
+ left: position?.left ?? 0,
1283
+ visibility: position ? 'visible' : 'hidden',
1284
+ }),
1285
+ }));
1286
+
1287
+ /**
1288
+ * Hook pour calculer la position d'un élément par rapport à son ancre
1289
+ * @param anchor - Élément HTML servant d'ancre
1290
+ * @param menuRef - Référence vers l'élément à positionner
1291
+ * @param anchorOrigin - Origine de l'ancrage ('left' ou 'right')
1292
+ * @param isVisible - Si l'élément est visible (pour recalculer la position)
1293
+ */
1294
+ const useAnchorPosition = ({ anchor, menuRef, anchorOrigin = 'right', isVisible }) => {
1295
+ const [position, setPosition] = React.useState(null);
1296
+ React.useLayoutEffect(() => {
1297
+ if (anchor && isVisible) {
1298
+ const anchorRect = anchor.getBoundingClientRect();
1299
+ const menuWidth = menuRef.current?.offsetWidth || 150;
1300
+ const left = anchorOrigin === 'right'
1301
+ ? anchorRect.right - menuWidth + window.scrollX
1302
+ : anchorRect.left + window.scrollX;
1303
+ setPosition({
1304
+ top: anchorRect.bottom + window.scrollY,
1305
+ left
1306
+ });
1307
+ }
1308
+ else {
1309
+ setPosition(null);
1310
+ }
1311
+ }, [anchor, anchorOrigin, isVisible, menuRef]);
1312
+ return position;
1313
+ };
1314
+
1315
+ /**
1316
+ * Gestion d'un click en dehors d'un ou plusieurs éléments
1317
+ * @param refs Tableau de références à écouter
1318
+ * @param onClickOutside Callback si clic à l'extérieur
1319
+ * @param shouldTrigger Activation du comportement
1320
+ */
1321
+ const useClickOutside = (refs, onClickOutside, shouldTrigger = true) => {
1322
+ React.useEffect(() => {
1323
+ if (!shouldTrigger) {
1324
+ return;
1325
+ }
1326
+ const handleClick = (event) => {
1327
+ const isInside = refs.some(ref => ref.current?.contains(event.target));
1328
+ if (!isInside) {
1329
+ onClickOutside();
1330
+ }
1331
+ };
1332
+ document.addEventListener('mousedown', handleClick);
1333
+ return () => {
1334
+ document.removeEventListener('mousedown', handleClick);
1335
+ };
1336
+ }, [refs, onClickOutside, shouldTrigger]);
1337
+ };
1338
+
1339
+ /**
1340
+ * Hook pour gérer les animations de transition lors des renders
1341
+ * @param isOpen - État d'ouverture
1342
+ * @param duration - Durée de la transition en ms
1343
+ */
1344
+ const useTransitionRender = (isOpen, duration = DEFAULT_TRANSITION_DURATION_MS) => {
1345
+ const [isVisible, setIsVisible] = React.useState(false);
1346
+ const [isFadingIn, setIsFadingIn] = React.useState(false);
1347
+ React.useEffect(() => {
1348
+ if (isOpen) {
1349
+ setIsVisible(true);
1350
+ const timeout = setTimeout(() => {
1351
+ setIsFadingIn(true);
1352
+ }, 10);
1353
+ return () => {
1354
+ clearTimeout(timeout);
1355
+ };
1356
+ }
1357
+ else {
1358
+ setIsFadingIn(false);
1359
+ const timeout = setTimeout(() => {
1360
+ setIsVisible(false);
1361
+ }, duration);
1362
+ return () => {
1363
+ clearTimeout(timeout);
1364
+ };
1365
+ }
1366
+ }, [isOpen, duration]);
1367
+ return {
1368
+ isVisible,
1369
+ isFadingIn
1370
+ };
1371
+ };
1372
+
1373
+ const Menu = ({ anchor, onClose, children, anchorOrigin = 'right' }) => {
1374
+ // refs
1375
+ const menuRef = React.useRef(null);
1376
+ const anchorRef = React.useRef(null);
1377
+ // variables
1378
+ const isOpen = Boolean(anchor);
1379
+ const refs = React.useMemo(() => [menuRef, anchorRef], []);
1380
+ // hooks
1381
+ const { isVisible, isFadingIn } = useTransitionRender(isOpen);
1382
+ const position = useAnchorPosition({ anchor: anchorRef.current, menuRef, anchorOrigin, isVisible });
1383
+ // useEffects
1384
+ React.useEffect(() => {
1385
+ if (anchor) {
1386
+ anchorRef.current = anchor;
1387
+ }
1388
+ }, [anchor]);
1389
+ React.useEffect(() => {
1390
+ if (!isVisible) {
1391
+ anchorRef.current = null;
1392
+ }
1393
+ }, [isVisible]);
1394
+ useClickOutside(refs, onClose, isVisible);
1395
+ if (!isVisible) {
1396
+ return null;
1397
+ }
1398
+ return (jsxRuntime.jsx("div", { ref: menuRef, className: MENU_STYLES.root(isFadingIn, anchorOrigin, position), children: children }));
1399
+ };
1400
+ Menu.displayName = 'Menu';
1401
+
1126
1402
  /**
1127
1403
  * DrawerItem styles using createStyles from @aurora-ds/theme
1128
1404
  */
@@ -1226,8 +1502,8 @@ const insertSeparators = (items, SeparatorComponent) => {
1226
1502
  const result = [];
1227
1503
  items.forEach((item, index) => {
1228
1504
  // Add the item with a key
1229
- const itemWithKey = react.isValidElement(item)
1230
- ? react.cloneElement(item, { key: `item-${index}` })
1505
+ const itemWithKey = React.isValidElement(item)
1506
+ ? React.cloneElement(item, { key: `item-${index}` })
1231
1507
  : item;
1232
1508
  result.push(itemWithKey);
1233
1509
  // Add separator after each item except the last one
@@ -1253,8 +1529,8 @@ const buildBreadcrumbChildren = (items, maxItems, EllipsisComponent, SeparatorCo
1253
1529
  // Build the result: first + separator + ellipsis + separator + last items with separators between them
1254
1530
  const result = [];
1255
1531
  // Add first item with key
1256
- const firstWithKey = react.isValidElement(firstItem)
1257
- ? react.cloneElement(firstItem, { key: 'first' })
1532
+ const firstWithKey = React.isValidElement(firstItem)
1533
+ ? React.cloneElement(firstItem, { key: 'first' })
1258
1534
  : firstItem;
1259
1535
  result.push(firstWithKey);
1260
1536
  // Add separator + ellipsis + separator
@@ -1263,8 +1539,8 @@ const buildBreadcrumbChildren = (items, maxItems, EllipsisComponent, SeparatorCo
1263
1539
  result.push(jsxRuntime.jsx(SeparatorComponent, {}, 'sep-ellipsis-after'));
1264
1540
  // Add last items with separators between them
1265
1541
  lastItems.forEach((item, index) => {
1266
- const itemWithKey = react.isValidElement(item)
1267
- ? react.cloneElement(item, { key: `last-${index}` })
1542
+ const itemWithKey = React.isValidElement(item)
1543
+ ? React.cloneElement(item, { key: `last-${index}` })
1268
1544
  : item;
1269
1545
  result.push(itemWithKey);
1270
1546
  // Add separator between items (but not after the last one)
@@ -1280,8 +1556,8 @@ const buildBreadcrumbChildren = (items, maxItems, EllipsisComponent, SeparatorCo
1280
1556
  */
1281
1557
  const flattenChildren = (children) => {
1282
1558
  const result = [];
1283
- react.Children.forEach(children, (child) => {
1284
- if (react.isValidElement(child) && child.type === react.Fragment) {
1559
+ React.Children.forEach(children, (child) => {
1560
+ if (React.isValidElement(child) && child.type === React.Fragment) {
1285
1561
  result.push(...flattenChildren(child.props.children));
1286
1562
  }
1287
1563
  else {
@@ -1295,7 +1571,7 @@ const flattenChildren = (children) => {
1295
1571
  * Checks if a React element is a BreadcrumbSeparator
1296
1572
  */
1297
1573
  const isSeparator = (child) => {
1298
- return react.isValidElement(child) && child.type?.displayName === 'BreadcrumbSeparator';
1574
+ return React.isValidElement(child) && child.type?.displayName === 'BreadcrumbSeparator';
1299
1575
  };
1300
1576
 
1301
1577
  /**
@@ -1307,11 +1583,11 @@ const isSeparator = (child) => {
1307
1583
  * @param children
1308
1584
  * @param props
1309
1585
  */
1310
- const Breadcrumb = react.memo(({ maxItems, children, ...props }) => {
1586
+ const Breadcrumb = React.memo(({ maxItems, children, ...props }) => {
1311
1587
  const allChildren = flattenChildren(children);
1312
1588
  // Filter out any manually added separators (in case user added them)
1313
1589
  const items = allChildren.filter(child => !isSeparator(child));
1314
- const renderedChildren = react.useMemo(() => buildBreadcrumbChildren(items, maxItems, BreadcrumbEllipsis, BreadcrumbSeparator), [items, maxItems]);
1590
+ const renderedChildren = React.useMemo(() => buildBreadcrumbChildren(items, maxItems, BreadcrumbEllipsis, BreadcrumbSeparator), [items, maxItems]);
1315
1591
  return (jsxRuntime.jsx("nav", { "aria-label": 'breadcrumb', ...props, children: jsxRuntime.jsx("ol", { className: BREADCRUMB_STYLES.list, children: renderedChildren }) }));
1316
1592
  });
1317
1593
  Breadcrumb.displayName = 'Breadcrumb';
@@ -1343,7 +1619,7 @@ const BREADCRUMB_LINK_STYLES = theme.createStyles((theme) => ({
1343
1619
  * BreadcrumbLink component
1344
1620
  * Renders a clickable breadcrumb item with a link
1345
1621
  */
1346
- const BreadcrumbLink = react.memo((props) => {
1622
+ const BreadcrumbLink = React.memo((props) => {
1347
1623
  const { children, onClick } = props;
1348
1624
  return (jsxRuntime.jsx("li", { className: BREADCRUMB_LINK_STYLES.item, children: jsxRuntime.jsx("a", { className: BREADCRUMB_LINK_STYLES.link, onClick: onClick, children: jsxRuntime.jsx(Text, { variant: 'span', fontSize: 'sm', children: children }) }) }));
1349
1625
  });
@@ -1368,10 +1644,75 @@ const BREADCRUMB_PAGE_STYLES = theme.createStyles((theme) => ({
1368
1644
  * BreadcrumbPage component
1369
1645
  * Renders the current page (non-clickable) in the breadcrumb
1370
1646
  */
1371
- const BreadcrumbPage = react.memo(({ children }) => (jsxRuntime.jsx("li", { className: BREADCRUMB_PAGE_STYLES.item, children: jsxRuntime.jsx("span", { className: BREADCRUMB_PAGE_STYLES.page, children: jsxRuntime.jsx(Text, { variant: 'span', fontSize: 'sm', color: 'primary', children: children }) }) })));
1647
+ const BreadcrumbPage = React.memo(({ children }) => (jsxRuntime.jsx("li", { className: BREADCRUMB_PAGE_STYLES.item, children: jsxRuntime.jsx("span", { className: BREADCRUMB_PAGE_STYLES.page, children: jsxRuntime.jsx(Text, { variant: 'span', fontSize: 'sm', color: 'primary', children: children }) }) })));
1372
1648
  BreadcrumbPage.displayName = 'BreadcrumbPage';
1373
1649
 
1650
+ /**
1651
+ * Tabs styles using createStyles from @aurora-ds/theme
1652
+ */
1653
+ const TABS_STYLES = theme.createStyles((theme) => ({
1654
+ root: ({ width = '100%' }) => ({
1655
+ display: 'flex',
1656
+ width,
1657
+ backgroundColor: theme.colors.surfaceHover,
1658
+ borderRadius: theme.radius.md,
1659
+ overflow: 'hidden',
1660
+ padding: theme.spacing.xs,
1661
+ height: `calc(2 * ${theme.spacing.xs} + ${BUTTON_SIZE}px)`,
1662
+ }),
1663
+ }));
1664
+
1665
+ /**
1666
+ * Tabs component for navigation between multiple sections
1667
+ */
1668
+ const Tabs = ({ children, activeTab, width = '100%', }) => {
1669
+ return (jsxRuntime.jsx("div", { className: TABS_STYLES.root({ width }), children: React.Children.map(children, (child, index) => {
1670
+ if (React.isValidElement(child)) {
1671
+ const tabValue = child.props.value;
1672
+ const isActive = activeTab === tabValue || (activeTab === undefined && index === 0);
1673
+ return React.cloneElement(child, {
1674
+ isActive,
1675
+ });
1676
+ }
1677
+ return child;
1678
+ }) }));
1679
+ };
1680
+ Tabs.displayName = 'Tabs';
1681
+
1682
+ /**
1683
+ * Tabs styles using createStyles from @aurora-ds/theme
1684
+ */
1685
+ const TAB_ITEM_STYLES = theme.createStyles((theme) => ({
1686
+ tab: (isActive) => ({
1687
+ flex: 1,
1688
+ padding: theme.spacing.sm,
1689
+ height: BUTTON_SIZE,
1690
+ borderRadius: theme.radius.sm,
1691
+ cursor: 'pointer',
1692
+ transition: `background-color ${theme.transition.fast}, color ${theme.transition.fast}, boxShadow ${theme.transition.fast}`,
1693
+ display: 'flex',
1694
+ alignItems: 'center',
1695
+ justifyContent: 'center',
1696
+ backgroundColor: isActive ? theme.colors.background : 'transparent',
1697
+ color: isActive ? theme.colors.text : theme.colors.textSecondary,
1698
+ boxShadow: isActive ? theme.shadows.sm : undefined,
1699
+ ':hover': {
1700
+ backgroundColor: !isActive ? theme.colors.secondaryActive : undefined,
1701
+ },
1702
+ }),
1703
+ }));
1704
+
1705
+ /**
1706
+ * TabItem component for use inside Tabs
1707
+ */
1708
+ const TabItem = ({ label, isActive = false, onClick, }) => {
1709
+ return (jsxRuntime.jsx("div", { className: TAB_ITEM_STYLES.tab(isActive), onClick: onClick, children: jsxRuntime.jsx(Text, { variant: 'label', preserveWhitespace: true, maxLines: 1, children: label }) }));
1710
+ };
1711
+ TabItem.displayName = 'TabItem';
1712
+
1374
1713
  exports.Accordion = Accordion;
1714
+ exports.Avatar = Avatar;
1715
+ exports.AvatarGroup = AvatarGroup;
1375
1716
  exports.Breadcrumb = Breadcrumb;
1376
1717
  exports.BreadcrumbEllipsis = BreadcrumbEllipsis;
1377
1718
  exports.BreadcrumbLink = BreadcrumbLink;
@@ -1386,9 +1727,16 @@ exports.Grid = Grid;
1386
1727
  exports.Icon = Icon;
1387
1728
  exports.IconButton = IconButton;
1388
1729
  exports.Input = Input_default;
1730
+ exports.Menu = Menu;
1389
1731
  exports.Page = Page;
1390
1732
  exports.PageSection = PageSection;
1733
+ exports.Separator = Separator;
1391
1734
  exports.Stack = Stack;
1735
+ exports.TabItem = TabItem;
1736
+ exports.Tabs = Tabs;
1392
1737
  exports.Text = Text;
1393
1738
  exports.TextArea = TextArea_default;
1739
+ exports.useAnchorPosition = useAnchorPosition;
1740
+ exports.useClickOutside = useClickOutside;
1741
+ exports.useTransitionRender = useTransitionRender;
1394
1742
  //# sourceMappingURL=index.js.map