@aurora-ds/components 0.15.4 → 0.16.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.
Files changed (55) hide show
  1. package/README.md +1 -0
  2. package/dist/cjs/components/actions/button/Button.props.d.ts +9 -0
  3. package/dist/cjs/components/actions/icon-button/IconButton.props.d.ts +9 -0
  4. package/dist/cjs/components/data-display/skeleton/Skeleton.d.ts +4 -0
  5. package/dist/cjs/components/data-display/skeleton/Skeleton.props.d.ts +7 -0
  6. package/dist/cjs/components/data-display/skeleton/Skeleton.styles.d.ts +3 -0
  7. package/dist/cjs/components/data-display/skeleton/index.d.ts +2 -0
  8. package/dist/cjs/components/forms/select/Select.d.ts +7 -0
  9. package/dist/cjs/components/forms/select/Select.props.d.ts +31 -0
  10. package/dist/cjs/components/forms/select/Select.styles.d.ts +10 -0
  11. package/dist/cjs/components/forms/select/SelectItem/SelectItem.d.ts +7 -0
  12. package/dist/cjs/components/forms/select/SelectItem/SelectItem.props.d.ts +19 -0
  13. package/dist/cjs/components/forms/select/SelectItem/SelectItem.styles.d.ts +7 -0
  14. package/dist/cjs/components/forms/select/SelectItem/index.d.ts +2 -0
  15. package/dist/cjs/components/forms/select/index.d.ts +2 -0
  16. package/dist/cjs/components/index.d.ts +3 -0
  17. package/dist/cjs/components/overlay/modal/Modal.d.ts +4 -0
  18. package/dist/cjs/components/overlay/modal/Modal.props.d.ts +13 -0
  19. package/dist/cjs/components/overlay/modal/Modal.styles.d.ts +4 -0
  20. package/dist/cjs/components/overlay/modal/index.d.ts +2 -0
  21. package/dist/cjs/constants/animations.d.ts +1 -0
  22. package/dist/cjs/index.js +410 -179
  23. package/dist/cjs/index.js.map +1 -1
  24. package/dist/cjs/interfaces/index.d.ts +1 -0
  25. package/dist/cjs/interfaces/select.types.d.ts +5 -0
  26. package/dist/cjs/resources/Icons.d.ts +2 -1
  27. package/dist/cjs/resources/icons/CloseIcon.d.ts +2 -0
  28. package/dist/esm/components/actions/button/Button.props.d.ts +9 -0
  29. package/dist/esm/components/actions/icon-button/IconButton.props.d.ts +9 -0
  30. package/dist/esm/components/data-display/skeleton/Skeleton.d.ts +4 -0
  31. package/dist/esm/components/data-display/skeleton/Skeleton.props.d.ts +7 -0
  32. package/dist/esm/components/data-display/skeleton/Skeleton.styles.d.ts +3 -0
  33. package/dist/esm/components/data-display/skeleton/index.d.ts +2 -0
  34. package/dist/esm/components/forms/select/Select.d.ts +7 -0
  35. package/dist/esm/components/forms/select/Select.props.d.ts +31 -0
  36. package/dist/esm/components/forms/select/Select.styles.d.ts +10 -0
  37. package/dist/esm/components/forms/select/SelectItem/SelectItem.d.ts +7 -0
  38. package/dist/esm/components/forms/select/SelectItem/SelectItem.props.d.ts +19 -0
  39. package/dist/esm/components/forms/select/SelectItem/SelectItem.styles.d.ts +7 -0
  40. package/dist/esm/components/forms/select/SelectItem/index.d.ts +2 -0
  41. package/dist/esm/components/forms/select/index.d.ts +2 -0
  42. package/dist/esm/components/index.d.ts +3 -0
  43. package/dist/esm/components/overlay/modal/Modal.d.ts +4 -0
  44. package/dist/esm/components/overlay/modal/Modal.props.d.ts +13 -0
  45. package/dist/esm/components/overlay/modal/Modal.styles.d.ts +4 -0
  46. package/dist/esm/components/overlay/modal/index.d.ts +2 -0
  47. package/dist/esm/constants/animations.d.ts +1 -0
  48. package/dist/esm/index.js +411 -183
  49. package/dist/esm/index.js.map +1 -1
  50. package/dist/esm/interfaces/index.d.ts +1 -0
  51. package/dist/esm/interfaces/select.types.d.ts +5 -0
  52. package/dist/esm/resources/Icons.d.ts +2 -1
  53. package/dist/esm/resources/icons/CloseIcon.d.ts +2 -0
  54. package/dist/index.d.ts +76 -3
  55. package/package.json +1 -1
package/dist/cjs/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var React = require('react');
5
5
  var theme = require('@aurora-ds/theme');
6
+ var reactDom = require('react-dom');
6
7
 
7
8
  const ICON_STYLES = theme.createStyles((theme) => ({
8
9
  root: (size, color, backgroundColor, padding, borderRadius) => ({
@@ -535,6 +536,31 @@ const AvatarGroup = ({ children, limit, size = 'medium' }) => {
535
536
  };
536
537
  AvatarGroup.displayName = 'AvatarGroup';
537
538
 
539
+ const shimmerAnimation = theme.keyframes({
540
+ '0%': {
541
+ backgroundPosition: '-200% 0',
542
+ },
543
+ '100%': {
544
+ backgroundPosition: '200% 0',
545
+ },
546
+ });
547
+
548
+ const SKELETON_STYLES = theme.createStyles((theme) => ({
549
+ root: (width, height, borderRadius) => ({
550
+ width,
551
+ height,
552
+ borderRadius: borderRadius ? theme.radius[borderRadius] : theme.radius.md,
553
+ background: `linear-gradient(90deg, ${theme.colors.surfaceActive} 25%, ${theme.colors.surfaceHover} 50%, ${theme.colors.surfaceActive} 75%)`,
554
+ backgroundSize: '200% 100%',
555
+ animation: `${shimmerAnimation} 1.2s ease-in-out infinite`,
556
+ }),
557
+ }));
558
+
559
+ const Skeleton = ({ width, height, borderRadius }) => {
560
+ return (jsxRuntime.jsx("div", { className: SKELETON_STYLES.root(width, height, borderRadius) }));
561
+ };
562
+ Skeleton.displayName = 'Skeleton';
563
+
538
564
  const getButtonSizeStyles = () => ({
539
565
  small: {
540
566
  height: BUTTON_SIZE - 8,
@@ -647,12 +673,15 @@ const BUTTON_STYLES = theme.createStyles((theme) => {
647
673
  const variantStyles = getButtonVariantStyles(theme);
648
674
  const sizeStyles = getButtonSizeStyles();
649
675
  return {
650
- root: ({ variant = 'contained', active = false, textColor, size = 'medium' }) => {
676
+ root: ({ variant = 'contained', active = false, textColor, backgroundColor, hoverBackgroundColor, activeBackgroundColor, size = 'medium' }) => {
651
677
  const sizeConfig = sizeStyles[size];
652
678
  const overrides = textColor ? {
653
679
  ...(variant !== 'contained' && { color: theme.colors[textColor] }),
654
680
  ...(variant === 'outlined' && { border: `1px solid ${theme.colors[textColor]}` }),
655
681
  } : {};
682
+ const backgroundOverride = backgroundColor ? { backgroundColor: theme.colors[backgroundColor] } : {};
683
+ const hoverBackgroundOverride = hoverBackgroundColor ? { backgroundColor: theme.colors[hoverBackgroundColor] } : {};
684
+ const activeBackgroundOverride = activeBackgroundColor ? { backgroundColor: theme.colors[activeBackgroundColor] } : {};
656
685
  return {
657
686
  display: 'inline-flex',
658
687
  alignItems: 'center',
@@ -668,10 +697,17 @@ const BUTTON_STYLES = theme.createStyles((theme) => {
668
697
  fontFamily: 'inherit',
669
698
  ...variantStyles[variant].default,
670
699
  ...(active && variantStyles[variant].pressed),
671
- ':hover': variantStyles[variant].hover,
672
- ':active': variantStyles[variant].pressed,
700
+ ':hover': {
701
+ ...variantStyles[variant].hover,
702
+ ...hoverBackgroundOverride,
703
+ },
704
+ ':active': {
705
+ ...variantStyles[variant].pressed,
706
+ ...activeBackgroundOverride,
707
+ },
673
708
  ':disabled': variantStyles[variant].disabled,
674
709
  ...overrides,
710
+ ...backgroundOverride,
675
711
  };
676
712
  },
677
713
  };
@@ -685,12 +721,12 @@ const BUTTON_STYLES = theme.createStyles((theme) => {
685
721
  * - `outlined`: Border only button
686
722
  * - `text`: Text only button without background
687
723
  */
688
- const Button = ({ label, startIcon, endIcon, variant = 'contained', active = false, onClick, disabled, type = 'button', textColor: customTextColor, size = 'medium', ariaLabel, ariaLabelledBy, ariaDescribedBy, role, tabIndex, }) => {
724
+ const Button = ({ label, startIcon, endIcon, variant = 'contained', active = false, onClick, disabled, type = 'button', textColor: customTextColor, backgroundColor, hoverBackgroundColor, activeBackgroundColor, size = 'medium', ariaLabel, ariaLabelledBy, ariaDescribedBy, role, tabIndex, }) => {
689
725
  const theme$1 = theme.useTheme();
690
726
  const variantStyles = getButtonVariantStyles(theme$1);
691
727
  const sizeStyles = getButtonSizeStyles();
692
728
  const textColor = disabled ? 'disabledText' : (customTextColor ?? variantStyles[variant].textColor);
693
- return (jsxRuntime.jsxs("button", { onClick: onClick, disabled: disabled, type: type, className: BUTTON_STYLES.root({ variant, active, textColor: customTextColor, size }), "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, children: [startIcon && (jsxRuntime.jsx(Icon, { color: textColor, children: startIcon })), jsxRuntime.jsx(Text, { variant: 'label', color: textColor, fontSize: sizeStyles[size].fontSize, children: label }), endIcon && (jsxRuntime.jsx(Icon, { color: textColor, children: endIcon }))] }));
729
+ return (jsxRuntime.jsxs("button", { onClick: onClick, disabled: disabled, type: type, className: BUTTON_STYLES.root({ variant, active, textColor: customTextColor, backgroundColor, hoverBackgroundColor, activeBackgroundColor, size }), "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, children: [startIcon && (jsxRuntime.jsx(Icon, { color: textColor, children: startIcon })), jsxRuntime.jsx(Text, { variant: 'label', color: textColor, fontSize: sizeStyles[size].fontSize, children: label }), endIcon && (jsxRuntime.jsx(Icon, { color: textColor, children: endIcon }))] }));
694
730
  };
695
731
  Button.displayName = 'Button';
696
732
 
@@ -716,12 +752,15 @@ const ICON_BUTTON_STYLES = theme.createStyles((theme) => {
716
752
  const variantStyles = getButtonVariantStyles(theme);
717
753
  const sizeStyles = getIconButtonSizeStyles();
718
754
  return {
719
- root: ({ variant = 'contained', active = false, size = 'medium', textColor }) => {
755
+ root: ({ variant = 'contained', active = false, size = 'medium', textColor, backgroundColor, hoverBackgroundColor, activeBackgroundColor }) => {
720
756
  const sizeConfig = sizeStyles[size];
721
757
  const overrides = textColor ? {
722
758
  ...(variant !== 'contained' && { color: theme.colors[textColor] }),
723
759
  ...(variant === 'outlined' && { border: `1px solid ${theme.colors[textColor]}` }),
724
760
  } : {};
761
+ const backgroundOverride = backgroundColor ? { backgroundColor: theme.colors[backgroundColor] } : {};
762
+ const hoverBackgroundOverride = hoverBackgroundColor ? { backgroundColor: theme.colors[hoverBackgroundColor] } : {};
763
+ const activeBackgroundOverride = activeBackgroundColor ? { backgroundColor: theme.colors[activeBackgroundColor] } : {};
725
764
  return {
726
765
  display: 'inline-flex',
727
766
  alignItems: 'center',
@@ -739,21 +778,28 @@ const ICON_BUTTON_STYLES = theme.createStyles((theme) => {
739
778
  fontFamily: 'inherit',
740
779
  ...variantStyles[variant].default,
741
780
  ...(active && variantStyles[variant].pressed),
742
- ':hover': variantStyles[variant].hover,
743
- ':active': variantStyles[variant].pressed,
781
+ ':hover': {
782
+ ...variantStyles[variant].hover,
783
+ ...hoverBackgroundOverride,
784
+ },
785
+ ':active': {
786
+ ...variantStyles[variant].pressed,
787
+ ...activeBackgroundOverride,
788
+ },
744
789
  ':disabled': variantStyles[variant].disabled,
745
790
  ...overrides,
791
+ ...backgroundOverride,
746
792
  };
747
793
  },
748
794
  };
749
795
  });
750
796
 
751
- const IconButton = ({ icon, variant = 'contained', active = false, type = 'button', onClick, disabled, textColor: customTextColor, size = 'medium', ariaLabel, ariaLabelledBy, ariaDescribedBy, role, tabIndex, }) => {
797
+ const IconButton = ({ icon, variant = 'contained', active = false, type = 'button', onClick, disabled, textColor: customTextColor, backgroundColor, hoverBackgroundColor, activeBackgroundColor, size = 'medium', ariaLabel, ariaLabelledBy, ariaDescribedBy, role, tabIndex, }) => {
752
798
  const theme$1 = theme.useTheme();
753
799
  const variantStyles = getButtonVariantStyles(theme$1);
754
800
  const textColor = disabled ? 'disabledText' : (customTextColor ?? variantStyles[variant].textColor);
755
801
  const iconSize = getIconButtonSizeStyles()[size].iconSize;
756
- return (jsxRuntime.jsx("button", { onClick: onClick, disabled: disabled, type: type, className: ICON_BUTTON_STYLES.root({ variant, active, size, textColor: customTextColor }), "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, children: jsxRuntime.jsx(Icon, { color: textColor, size: iconSize, children: icon }) }));
802
+ return (jsxRuntime.jsx("button", { onClick: onClick, disabled: disabled, type: type, className: ICON_BUTTON_STYLES.root({ variant, active, size, textColor: customTextColor, backgroundColor, hoverBackgroundColor, activeBackgroundColor }), "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, role: role, tabIndex: tabIndex, children: jsxRuntime.jsx(Icon, { color: textColor, size: iconSize, children: icon }) }));
757
803
  };
758
804
  IconButton.displayName = 'IconButton';
759
805
 
@@ -774,7 +820,7 @@ const Form = ({ children, onSubmit, ariaLabel, }) => {
774
820
  return (jsxRuntime.jsx("form", { onSubmit: handleSubmit, className: FORM_STYLES.root, "aria-label": ariaLabel, children: children }));
775
821
  };
776
822
  Form.displayName = 'Form';
777
- var Form_default = React.memo(Form);
823
+ var Form$1 = React.memo(Form);
778
824
 
779
825
  /**
780
826
  * Input styles using createStyles from @aurora-ds/theme
@@ -899,6 +945,8 @@ const ChevronRightIcon = () => {
899
945
  return (jsxRuntime.jsx("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', "stroke-width": '2', "stroke-linecap": 'round', "stroke-linejoin": 'round', className: 'lucide lucide-chevron-right-icon lucide-chevron-right', children: jsxRuntime.jsx("path", { d: 'm9 18 6-6-6-6' }) }));
900
946
  };
901
947
 
948
+ const CloseIcon = () => (jsxRuntime.jsx("svg", { width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg', children: jsxRuntime.jsx("path", { d: 'M12 4L4 12M4 4l8 8', stroke: 'currentColor', strokeWidth: '2', strokeLinecap: 'round', strokeLinejoin: 'round' }) }));
949
+
902
950
  const EyeIcon = () => {
903
951
  return (jsxRuntime.jsxs("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', "stroke-width": '2', "stroke-linecap": 'round', "stroke-linejoin": 'round', className: 'lucide lucide-eye-icon lucide-eye', children: [jsxRuntime.jsx("path", { d: 'M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0' }), jsxRuntime.jsx("circle", { cx: '12', cy: '12', r: '3' })] }));
904
952
  };
@@ -1004,6 +1052,302 @@ const TextArea = React.forwardRef(({ value, onChange, label, mandatory = false,
1004
1052
  TextArea.displayName = 'TextArea';
1005
1053
  var TextArea_default = React.memo(TextArea);
1006
1054
 
1055
+ /**
1056
+ * Select styles using createStyles from @aurora-ds/theme
1057
+ */
1058
+ const SELECT_STYLES = theme.createStyles((theme) => ({
1059
+ root: ({ disabled = false, width }) => ({
1060
+ position: 'relative',
1061
+ display: 'inline-flex',
1062
+ alignItems: 'center',
1063
+ justifyContent: 'space-between',
1064
+ boxSizing: 'border-box',
1065
+ width: width ?? '100%',
1066
+ padding: `${theme.spacing.sm} ${theme.spacing.md}`,
1067
+ border: `1px solid ${theme.colors.border}`,
1068
+ borderRadius: theme.radius.md,
1069
+ fontSize: theme.fontSize.md,
1070
+ fontFamily: 'inherit',
1071
+ backgroundColor: theme.colors.surface,
1072
+ color: theme.colors.text,
1073
+ cursor: disabled ? 'not-allowed' : 'pointer',
1074
+ transition: `border-color ${theme.transition.fast}`,
1075
+ outline: 'none',
1076
+ minHeight: BUTTON_SIZE,
1077
+ maxHeight: BUTTON_SIZE,
1078
+ gap: theme.spacing.md,
1079
+ lineHeight: theme.lineHeight.none,
1080
+ textOverflow: 'ellipsis',
1081
+ overflow: 'hidden',
1082
+ whiteSpace: 'nowrap',
1083
+ ...(disabled && {
1084
+ color: theme.colors.disabled,
1085
+ cursor: 'not-allowed',
1086
+ opacity: theme.opacity.high
1087
+ }),
1088
+ ...(!disabled && {
1089
+ ':hover': {
1090
+ borderColor: theme.colors.primaryHover,
1091
+ },
1092
+ }),
1093
+ }),
1094
+ trigger: {
1095
+ display: 'flex',
1096
+ alignItems: 'center',
1097
+ gap: theme.spacing.sm,
1098
+ flex: 1
1099
+ },
1100
+ value: {
1101
+ flex: 1,
1102
+ fontSize: theme.fontSize.md,
1103
+ color: theme.colors.text
1104
+ },
1105
+ placeholder: {
1106
+ flex: 1,
1107
+ fontSize: theme.fontSize.md,
1108
+ color: theme.colors.textSecondary
1109
+ }
1110
+ }));
1111
+
1112
+ /**
1113
+ * SelectItem styles using createStyles from @aurora-ds/theme
1114
+ */
1115
+ const SELECT_ITEM_STYLES = theme.createStyles((theme) => ({
1116
+ root: ({ isSelected = false, disabled = false }) => ({
1117
+ display: 'flex',
1118
+ alignItems: 'center',
1119
+ gap: theme.spacing.sm,
1120
+ padding: theme.spacing.sm,
1121
+ borderRadius: theme.radius.sm,
1122
+ backgroundColor: isSelected ? theme.colors.primary : theme.colors.surface,
1123
+ outline: 'none',
1124
+ border: 'none',
1125
+ cursor: disabled ? 'not-allowed' : 'pointer',
1126
+ minHeight: MENU_ITEM_SIZE,
1127
+ maxHeight: MENU_ITEM_SIZE,
1128
+ flexShrink: 0,
1129
+ transition: 'background-color 150ms ease-in-out',
1130
+ color: disabled ? theme.colors.disabled : (isSelected ? theme.colors.surface : theme.colors.text),
1131
+ opacity: disabled ? 0.5 : 1,
1132
+ ':hover': {
1133
+ backgroundColor: disabled ? theme.colors.surface : (isSelected ? theme.colors.primary : theme.colors.surfaceHover),
1134
+ },
1135
+ })
1136
+ }));
1137
+
1138
+ /**
1139
+ * SelectItem component for use inside Select dropdown
1140
+ */
1141
+ const SelectItem = ({ option, isSelected = false, onSelect }) => {
1142
+ const handleClick = () => {
1143
+ if (!option.disabled && onSelect) {
1144
+ onSelect(option.value);
1145
+ }
1146
+ };
1147
+ return (jsxRuntime.jsx("button", { className: SELECT_ITEM_STYLES.root({ isSelected, disabled: option.disabled }), onClick: handleClick, role: 'option', "aria-selected": isSelected, children: jsxRuntime.jsx(Text, { variant: 'label', maxLines: 1, children: option.label }) }));
1148
+ };
1149
+ SelectItem.displayName = 'SelectItem';
1150
+
1151
+ const MENU_STYLES = theme.createStyles((theme) => ({
1152
+ root: (isFadingIn, anchorOrigin, position, width) => ({
1153
+ position: 'absolute',
1154
+ zIndex: theme.zIndex.dropdown,
1155
+ marginTop: theme.spacing.xs,
1156
+ backgroundColor: theme.colors.surface,
1157
+ borderRadius: theme.radius.md,
1158
+ border: `1px solid ${theme.colors.border}`,
1159
+ boxShadow: theme.shadows.md,
1160
+ minWidth: 150,
1161
+ maxHeight: MENU_ITEM_SIZE * 8,
1162
+ overflowY: 'auto',
1163
+ width,
1164
+ opacity: isFadingIn ? 1 : 0,
1165
+ transform: isFadingIn ? 'scale(1)' : 'scale(0.95)',
1166
+ transformOrigin: anchorOrigin === 'right' ? 'top right' : 'top left',
1167
+ transition: `opacity ${DEFAULT_TRANSITION_DURATION_MS}ms ease-out, transform ${DEFAULT_TRANSITION_DURATION_MS}ms ease-out`,
1168
+ top: position?.top ?? 0,
1169
+ left: position?.left ?? 0,
1170
+ visibility: position ? 'visible' : 'hidden',
1171
+ }),
1172
+ }));
1173
+
1174
+ /**
1175
+ * Hook pour calculer la position d'un élément par rapport à son ancre
1176
+ * @param anchor - Élément HTML servant d'ancre
1177
+ * @param menuRef - Référence vers l'élément à positionner
1178
+ * @param anchorOrigin - Origine de l'ancrage ('left' ou 'right')
1179
+ * @param isVisible - Si l'élément est visible (pour recalculer la position)
1180
+ */
1181
+ const useAnchorPosition = ({ anchor, menuRef, anchorOrigin = 'right', isVisible }) => {
1182
+ const [position, setPosition] = React.useState(null);
1183
+ React.useLayoutEffect(() => {
1184
+ if (anchor && isVisible) {
1185
+ const anchorRect = anchor.getBoundingClientRect();
1186
+ const menuWidth = menuRef.current?.offsetWidth || 150;
1187
+ const left = anchorOrigin === 'right'
1188
+ ? anchorRect.right - menuWidth + window.scrollX
1189
+ : anchorRect.left + window.scrollX;
1190
+ setPosition({
1191
+ top: anchorRect.bottom + window.scrollY,
1192
+ left
1193
+ });
1194
+ }
1195
+ else {
1196
+ setPosition(null);
1197
+ }
1198
+ }, [anchor, anchorOrigin, isVisible, menuRef]);
1199
+ return position;
1200
+ };
1201
+
1202
+ /**
1203
+ * Gestion d'un click en dehors d'un ou plusieurs éléments
1204
+ * @param refs Tableau de références à écouter
1205
+ * @param onClickOutside Callback si clic à l'extérieur
1206
+ * @param shouldTrigger Activation du comportement
1207
+ */
1208
+ const useClickOutside = (refs, onClickOutside, shouldTrigger = true) => {
1209
+ React.useEffect(() => {
1210
+ if (!shouldTrigger) {
1211
+ return;
1212
+ }
1213
+ const handleClick = (event) => {
1214
+ const isInside = refs.some(ref => ref.current?.contains(event.target));
1215
+ if (!isInside) {
1216
+ onClickOutside();
1217
+ }
1218
+ };
1219
+ document.addEventListener('mousedown', handleClick);
1220
+ return () => {
1221
+ document.removeEventListener('mousedown', handleClick);
1222
+ };
1223
+ }, [refs, onClickOutside, shouldTrigger]);
1224
+ };
1225
+
1226
+ /**
1227
+ * Hook pour gérer les animations de transition lors des renders
1228
+ * @param isOpen - État d'ouverture
1229
+ * @param duration - Durée de la transition en ms
1230
+ */
1231
+ const useTransitionRender = (isOpen, duration = DEFAULT_TRANSITION_DURATION_MS) => {
1232
+ const [isVisible, setIsVisible] = React.useState(false);
1233
+ const [isFadingIn, setIsFadingIn] = React.useState(false);
1234
+ React.useEffect(() => {
1235
+ if (isOpen) {
1236
+ setIsVisible(true);
1237
+ const timeout = setTimeout(() => {
1238
+ setIsFadingIn(true);
1239
+ }, 10);
1240
+ return () => {
1241
+ clearTimeout(timeout);
1242
+ };
1243
+ }
1244
+ else {
1245
+ setIsFadingIn(false);
1246
+ const timeout = setTimeout(() => {
1247
+ setIsVisible(false);
1248
+ }, duration);
1249
+ return () => {
1250
+ clearTimeout(timeout);
1251
+ };
1252
+ }
1253
+ }, [isOpen, duration]);
1254
+ return {
1255
+ isVisible,
1256
+ isFadingIn
1257
+ };
1258
+ };
1259
+
1260
+ const Menu = ({ anchor, onClose, children, anchorOrigin = 'right', width }) => {
1261
+ // refs
1262
+ const menuRef = React.useRef(null);
1263
+ const anchorRef = React.useRef(null);
1264
+ // variables
1265
+ const isOpen = Boolean(anchor);
1266
+ const refs = React.useMemo(() => [menuRef, anchorRef], []);
1267
+ // hooks
1268
+ const { isVisible, isFadingIn } = useTransitionRender(isOpen);
1269
+ const position = useAnchorPosition({ anchor: anchorRef.current, menuRef, anchorOrigin, isVisible });
1270
+ // useEffects
1271
+ React.useEffect(() => {
1272
+ if (anchor) {
1273
+ anchorRef.current = anchor;
1274
+ }
1275
+ }, [anchor]);
1276
+ React.useEffect(() => {
1277
+ if (!isVisible) {
1278
+ anchorRef.current = null;
1279
+ }
1280
+ }, [isVisible]);
1281
+ useClickOutside(refs, onClose, isVisible);
1282
+ if (!isVisible) {
1283
+ return null;
1284
+ }
1285
+ return (jsxRuntime.jsx("div", { ref: menuRef, className: MENU_STYLES.root(isFadingIn, anchorOrigin, position, width), children: children }));
1286
+ };
1287
+ Menu.displayName = 'Menu';
1288
+
1289
+ const MENU_GROUP_STYLES = theme.createStyles((theme) => ({
1290
+ root: {
1291
+ display: 'flex',
1292
+ flexDirection: 'column',
1293
+ padding: theme.spacing.xs,
1294
+ },
1295
+ }));
1296
+
1297
+ const MenuGroup = ({ children }) => {
1298
+ return (jsxRuntime.jsx("div", { className: MENU_GROUP_STYLES.root, children: children }));
1299
+ };
1300
+ MenuGroup.displayName = 'MenuGroup';
1301
+
1302
+ const MENU_ITEM_STYLES = theme.createStyles((theme) => ({
1303
+ root: {
1304
+ display: 'flex',
1305
+ alignItems: 'center',
1306
+ gap: theme.spacing.sm,
1307
+ padding: theme.spacing.sm,
1308
+ borderRadius: theme.radius.sm,
1309
+ backgroundColor: theme.colors.surface,
1310
+ outline: 'none',
1311
+ border: 'none',
1312
+ cursor: 'pointer',
1313
+ minHeight: MENU_ITEM_SIZE,
1314
+ maxHeight: MENU_ITEM_SIZE,
1315
+ flexShrink: 0,
1316
+ transition: 'background-color 150ms ease-in-out',
1317
+ ':hover': {
1318
+ backgroundColor: theme.colors.surfaceHover,
1319
+ },
1320
+ },
1321
+ }));
1322
+
1323
+ const MenuItem = ({ label, icon, onClick, textColor, iconColor }) => {
1324
+ return (jsxRuntime.jsxs("button", { className: MENU_ITEM_STYLES.root, onClick: onClick, children: [icon && (jsxRuntime.jsx(Icon, { color: iconColor ?? 'text', size: 'sm', children: icon })), jsxRuntime.jsx(Text, { variant: 'label', color: textColor ?? 'text', maxLines: 1, fontSize: 'sm', children: label })] }));
1325
+ };
1326
+ MenuItem.displayName = 'MenuItem';
1327
+
1328
+ /**
1329
+ * Select component that uses Menu for dropdown
1330
+ */
1331
+ const Select = ({ options, value, onChange, placeholder = 'Select an option', disabled = false, width }) => {
1332
+ const [isOpen, setIsOpen] = React.useState(false);
1333
+ const triggerRef = React.useRef(null);
1334
+ const selectedOption = options.find(option => option.value === value);
1335
+ const handleTriggerClick = () => {
1336
+ if (!disabled) {
1337
+ setIsOpen(!isOpen);
1338
+ }
1339
+ };
1340
+ const handleClose = () => {
1341
+ setIsOpen(false);
1342
+ };
1343
+ const handleSelect = (selectedValue) => {
1344
+ onChange?.(selectedValue);
1345
+ setIsOpen(false);
1346
+ };
1347
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.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: [jsxRuntime.jsx("div", { className: SELECT_STYLES.trigger, children: jsxRuntime.jsx(Text, { variant: 'p', maxLines: 1, color: selectedOption ? 'text' : 'textSecondary', children: selectedOption ? selectedOption.label : placeholder }) }), jsxRuntime.jsx(Icon, { children: jsxRuntime.jsx(ChevronDownIcon, {}) })] }), jsxRuntime.jsx(Menu, { anchor: isOpen ? triggerRef.current : null, onClose: handleClose, width: width, children: jsxRuntime.jsx(MenuGroup, { children: options.map(option => (jsxRuntime.jsx(SelectItem, { option: option, isSelected: option.value === value, onSelect: handleSelect }, option.value))) }) })] }));
1348
+ };
1349
+ Select.displayName = 'Select';
1350
+
1007
1351
  /**
1008
1352
  * Card styles using createStyles from @aurora-ds/theme
1009
1353
  */
@@ -1035,7 +1379,7 @@ const CARD_STYLES = theme.createStyles((theme) => ({
1035
1379
  * - `column`: Vertical layout (default)
1036
1380
  * - `row`: Horizontal layout
1037
1381
  */
1038
- const Card = ({ children, direction = 'column', padding = 'md', width, height, gap, radius = 'md', shadow = 'sm', align, justify, backgroundColor = 'surface', borderColor, ariaLabel, ariaLabelledBy, ariaDescribedBy, role, tabIndex, }) => {
1382
+ const Card = ({ children, direction = 'column', padding = 'md', width, height, gap, radius = 'md', shadow = 'none', align, justify, backgroundColor = 'surface', borderColor = 'border', ariaLabel, ariaLabelledBy, ariaDescribedBy, role, tabIndex, }) => {
1039
1383
  return (jsxRuntime.jsx("div", { className: CARD_STYLES.root({
1040
1384
  direction,
1041
1385
  padding,
@@ -1265,182 +1609,66 @@ const Accordion = ({ title, children, expanded, defaultExpanded = false, onChang
1265
1609
  };
1266
1610
  Accordion.displayName = 'Accordion';
1267
1611
 
1268
- const MENU_STYLES = theme.createStyles((theme) => ({
1269
- root: (isFadingIn, anchorOrigin, position, width) => ({
1270
- position: 'absolute',
1271
- zIndex: theme.zIndex.dropdown,
1272
- marginTop: theme.spacing.xs,
1273
- backgroundColor: theme.colors.surface,
1274
- borderRadius: theme.radius.md,
1275
- border: `1px solid ${theme.colors.border}`,
1276
- boxShadow: theme.shadows.md,
1277
- minWidth: 150,
1278
- maxHeight: MENU_ITEM_SIZE * 8,
1279
- overflowY: 'auto',
1280
- width,
1612
+ const MODAL_STYLES = theme.createStyles((theme) => ({
1613
+ background: (isFadingIn) => ({
1614
+ background: 'rgba(0, 0, 0, 0.6)',
1615
+ display: 'flex',
1616
+ alignItems: 'center',
1617
+ justifyContent: 'center',
1618
+ position: 'fixed',
1619
+ top: 0,
1620
+ left: 0,
1621
+ zIndex: 999999,
1622
+ transition: 'opacity 150ms ease-in-out',
1623
+ width: '100vw',
1624
+ height: '100vh',
1281
1625
  opacity: isFadingIn ? 1 : 0,
1282
- transform: isFadingIn ? 'scale(1)' : 'scale(0.95)',
1283
- transformOrigin: anchorOrigin === 'right' ? 'top right' : 'top left',
1284
- transition: `opacity ${DEFAULT_TRANSITION_DURATION_MS}ms ease-out, transform ${DEFAULT_TRANSITION_DURATION_MS}ms ease-out`,
1285
- top: position?.top ?? 0,
1286
- left: position?.left ?? 0,
1287
- visibility: position ? 'visible' : 'hidden',
1626
+ }),
1627
+ content: (isFadingIn) => ({
1628
+ width: '100%',
1629
+ maxWidth: 500,
1630
+ borderRadius: 8,
1631
+ position: 'absolute',
1632
+ left: '50%',
1633
+ top: '50%',
1634
+ transform: isFadingIn ? 'translate(-50%, -50%) scale(1)' : 'translate(-50%, -50%) scale(0.95)',
1635
+ transformOrigin: 'center center',
1636
+ transition: 'transform 150ms ease-in-out',
1637
+ backgroundColor: theme.colors.background,
1638
+ padding: theme.spacing.md,
1639
+ display: 'flex',
1640
+ flexDirection: 'column',
1641
+ height: 'fit-content',
1642
+ alignItems: 'flex-start',
1643
+ gap: theme.spacing.md,
1288
1644
  }),
1289
1645
  }));
1290
1646
 
1291
- /**
1292
- * Hook pour calculer la position d'un élément par rapport à son ancre
1293
- * @param anchor - Élément HTML servant d'ancre
1294
- * @param menuRef - Référence vers l'élément à positionner
1295
- * @param anchorOrigin - Origine de l'ancrage ('left' ou 'right')
1296
- * @param isVisible - Si l'élément est visible (pour recalculer la position)
1297
- */
1298
- const useAnchorPosition = ({ anchor, menuRef, anchorOrigin = 'right', isVisible }) => {
1299
- const [position, setPosition] = React.useState(null);
1300
- React.useLayoutEffect(() => {
1301
- if (anchor && isVisible) {
1302
- const anchorRect = anchor.getBoundingClientRect();
1303
- const menuWidth = menuRef.current?.offsetWidth || 150;
1304
- const left = anchorOrigin === 'right'
1305
- ? anchorRect.right - menuWidth + window.scrollX
1306
- : anchorRect.left + window.scrollX;
1307
- setPosition({
1308
- top: anchorRect.bottom + window.scrollY,
1309
- left
1310
- });
1311
- }
1312
- else {
1313
- setPosition(null);
1314
- }
1315
- }, [anchor, anchorOrigin, isVisible, menuRef]);
1316
- return position;
1317
- };
1318
-
1319
- /**
1320
- * Gestion d'un click en dehors d'un ou plusieurs éléments
1321
- * @param refs Tableau de références à écouter
1322
- * @param onClickOutside Callback si clic à l'extérieur
1323
- * @param shouldTrigger Activation du comportement
1324
- */
1325
- const useClickOutside = (refs, onClickOutside, shouldTrigger = true) => {
1326
- React.useEffect(() => {
1327
- if (!shouldTrigger) {
1328
- return;
1329
- }
1330
- const handleClick = (event) => {
1331
- const isInside = refs.some(ref => ref.current?.contains(event.target));
1332
- if (!isInside) {
1333
- onClickOutside();
1334
- }
1335
- };
1336
- document.addEventListener('mousedown', handleClick);
1337
- return () => {
1338
- document.removeEventListener('mousedown', handleClick);
1339
- };
1340
- }, [refs, onClickOutside, shouldTrigger]);
1341
- };
1342
-
1343
- /**
1344
- * Hook pour gérer les animations de transition lors des renders
1345
- * @param isOpen - État d'ouverture
1346
- * @param duration - Durée de la transition en ms
1347
- */
1348
- const useTransitionRender = (isOpen, duration = DEFAULT_TRANSITION_DURATION_MS) => {
1349
- const [isVisible, setIsVisible] = React.useState(false);
1350
- const [isFadingIn, setIsFadingIn] = React.useState(false);
1351
- React.useEffect(() => {
1352
- if (isOpen) {
1353
- setIsVisible(true);
1354
- const timeout = setTimeout(() => {
1355
- setIsFadingIn(true);
1356
- }, 10);
1357
- return () => {
1358
- clearTimeout(timeout);
1359
- };
1360
- }
1361
- else {
1362
- setIsFadingIn(false);
1363
- const timeout = setTimeout(() => {
1364
- setIsVisible(false);
1365
- }, duration);
1366
- return () => {
1367
- clearTimeout(timeout);
1368
- };
1369
- }
1370
- }, [isOpen, duration]);
1371
- return {
1372
- isVisible,
1373
- isFadingIn
1374
- };
1375
- };
1376
-
1377
- const Menu = ({ anchor, onClose, children, anchorOrigin = 'right', width }) => {
1647
+ const Modal = ({ isOpen, onClose, label, children, isForm, action }) => {
1378
1648
  // refs
1379
- const menuRef = React.useRef(null);
1380
- const anchorRef = React.useRef(null);
1381
- // variables
1382
- const isOpen = Boolean(anchor);
1383
- const refs = React.useMemo(() => [menuRef, anchorRef], []);
1649
+ const modalRef = React.useRef(null);
1384
1650
  // hooks
1385
1651
  const { isVisible, isFadingIn } = useTransitionRender(isOpen);
1386
- const position = useAnchorPosition({ anchor: anchorRef.current, menuRef, anchorOrigin, isVisible });
1387
- // useEffects
1388
1652
  React.useEffect(() => {
1389
- if (anchor) {
1390
- anchorRef.current = anchor;
1391
- }
1392
- }, [anchor]);
1393
- React.useEffect(() => {
1394
- if (!isVisible) {
1395
- anchorRef.current = null;
1653
+ const handleKeyDown = (event) => {
1654
+ if (event.key === 'Escape' && isOpen) {
1655
+ onClose();
1656
+ }
1657
+ };
1658
+ document.addEventListener('keydown', handleKeyDown);
1659
+ return () => document.removeEventListener('keydown', handleKeyDown);
1660
+ }, [isOpen, onClose]);
1661
+ // components
1662
+ const Wrapper = isForm ? Form$1 : React.Fragment;
1663
+ const wrapperProps = isForm ? {
1664
+ onSubmit: (e) => {
1665
+ e.preventDefault();
1666
+ action?.onClick();
1396
1667
  }
1397
- }, [isVisible]);
1398
- useClickOutside(refs, onClose, isVisible);
1399
- if (!isVisible) {
1400
- return null;
1401
- }
1402
- return (jsxRuntime.jsx("div", { ref: menuRef, className: MENU_STYLES.root(isFadingIn, anchorOrigin, position, width), children: children }));
1403
- };
1404
- Menu.displayName = 'Menu';
1405
-
1406
- const MENU_GROUP_STYLES = theme.createStyles((theme) => ({
1407
- root: {
1408
- display: 'flex',
1409
- flexDirection: 'column',
1410
- padding: theme.spacing.xs,
1411
- },
1412
- }));
1413
-
1414
- const MenuGroup = ({ children }) => {
1415
- return (jsxRuntime.jsx("div", { className: MENU_GROUP_STYLES.root, children: children }));
1668
+ } : {};
1669
+ return reactDom.createPortal(jsxRuntime.jsx(React.Fragment, { children: isVisible ? (jsxRuntime.jsx("div", { className: MODAL_STYLES.background(isFadingIn), ref: modalRef, children: jsxRuntime.jsx("div", { className: MODAL_STYLES.content(isFadingIn), children: jsxRuntime.jsxs(Wrapper, { ...wrapperProps, children: [jsxRuntime.jsxs(Stack, { justify: 'space-between', height: BUTTON_SIZE, width: '100%', children: [jsxRuntime.jsx(Text, { variant: 'h3', children: label }), !action && (jsxRuntime.jsx(IconButton, { icon: jsxRuntime.jsx(CloseIcon, {}), onClick: onClose, size: 'small', variant: 'text', textColor: 'text' }))] }), children, action && (jsxRuntime.jsxs(Stack, { justify: 'flex-end', children: [jsxRuntime.jsx(Button, { label: 'Cancel', onClick: onClose, variant: 'outlined' }), jsxRuntime.jsx(Button, { label: action.label, onClick: !isForm ? action.onClick : undefined, type: isForm ? 'submit' : 'button', disabled: action.disabled })] }))] }) }) })) : null }), document.body);
1416
1670
  };
1417
- MenuGroup.displayName = 'MenuGroup';
1418
-
1419
- const MENU_ITEM_STYLES = theme.createStyles((theme) => ({
1420
- root: {
1421
- display: 'flex',
1422
- alignItems: 'center',
1423
- gap: theme.spacing.sm,
1424
- padding: theme.spacing.sm,
1425
- borderRadius: theme.radius.sm,
1426
- backgroundColor: theme.colors.surface,
1427
- outline: 'none',
1428
- border: 'none',
1429
- cursor: 'pointer',
1430
- minHeight: MENU_ITEM_SIZE,
1431
- maxHeight: MENU_ITEM_SIZE,
1432
- flexShrink: 0,
1433
- transition: 'background-color 150ms ease-in-out',
1434
- ':hover': {
1435
- backgroundColor: theme.colors.surfaceHover,
1436
- },
1437
- },
1438
- }));
1439
-
1440
- const MenuItem = ({ label, icon, onClick, textColor, iconColor }) => {
1441
- return (jsxRuntime.jsxs("button", { className: MENU_ITEM_STYLES.root, onClick: onClick, children: [icon && (jsxRuntime.jsx(Icon, { color: iconColor ?? 'text', size: 'sm', children: icon })), jsxRuntime.jsx(Text, { variant: 'label', color: textColor ?? 'text', maxLines: 1, fontSize: 'sm', children: label })] }));
1442
- };
1443
- MenuItem.displayName = 'MenuItem';
1671
+ Modal.displayName = 'Modal';
1444
1672
 
1445
1673
  /**
1446
1674
  * DrawerItem styles using createStyles from @aurora-ds/theme
@@ -1765,7 +1993,7 @@ exports.Button = Button;
1765
1993
  exports.Card = Card;
1766
1994
  exports.Chip = Chip;
1767
1995
  exports.DrawerItem = DrawerItem;
1768
- exports.Form = Form_default;
1996
+ exports.Form = Form$1;
1769
1997
  exports.Grid = Grid;
1770
1998
  exports.Icon = Icon;
1771
1999
  exports.IconButton = IconButton;
@@ -1773,9 +2001,12 @@ exports.Input = Input_default;
1773
2001
  exports.Menu = Menu;
1774
2002
  exports.MenuGroup = MenuGroup;
1775
2003
  exports.MenuItem = MenuItem;
2004
+ exports.Modal = Modal;
1776
2005
  exports.Page = Page;
1777
2006
  exports.PageSection = PageSection;
2007
+ exports.Select = Select;
1778
2008
  exports.Separator = Separator;
2009
+ exports.Skeleton = Skeleton;
1779
2010
  exports.Stack = Stack;
1780
2011
  exports.TabItem = TabItem;
1781
2012
  exports.Tabs = Tabs;