@lumx/react 4.4.1-alpha.0 → 4.4.1-alpha.2

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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Kind as Kind$1, Size as Size$1, ColorPalette as ColorPalette$1, Emphasis as Emphasis$1, Theme as Theme$1, AspectRatio as AspectRatio$1, ColorVariant, DIALOG_TRANSITION_DURATION, Orientation as Orientation$1, Alignment, NOTIFICATION_TRANSITION_DURATION, TOOLTIP_LONG_PRESS_DELAY, TOOLTIP_HOVER_DELAY } from '@lumx/core/js/constants';
1
+ import { Kind as Kind$1, Size as Size$1, ColorPalette as ColorPalette$1, Emphasis as Emphasis$1, ColorVariant, VISUALLY_HIDDEN, Theme as Theme$1, AspectRatio as AspectRatio$1, DOCUMENT, WINDOW, DIALOG_TRANSITION_DURATION, IS_BROWSER as IS_BROWSER$1, Orientation as Orientation$1, Alignment, NOTIFICATION_TRANSITION_DURATION } from '@lumx/core/js/constants';
2
2
  export * from '@lumx/core/js/constants';
3
3
  export * from '@lumx/core/js/types';
4
4
  import * as React from 'react';
@@ -16,7 +16,7 @@ import concat from 'lodash/concat.js';
16
16
  import dropRight from 'lodash/dropRight.js';
17
17
  import partition from 'lodash/partition.js';
18
18
  import reduce from 'lodash/reduce.js';
19
- import { u as useDisabledStateContext, P as Portal, C as ClickAwayProvider } from './_internal/DpdvhbTO.js';
19
+ import { u as useDisabledStateContext, P as Portal, C as ClickAwayProvider } from './_internal/BfgxEBp6.js';
20
20
  import isEmpty from 'lodash/isEmpty.js';
21
21
  import { getDisabledState } from '@lumx/core/js/utils/disabledState';
22
22
  import { mdiCloseCircle } from '@lumx/icons/esm/close-circle.js';
@@ -553,149 +553,6 @@ AutocompleteMultiple.displayName = COMPONENT_NAME$1i;
553
553
  AutocompleteMultiple.className = CLASSNAME$1h;
554
554
  AutocompleteMultiple.defaultProps = DEFAULT_PROPS$18;
555
555
 
556
- /**
557
- * Component display name.
558
- */
559
- const COMPONENT_NAME$1h = 'Avatar';
560
-
561
- /**
562
- * Component default class name and class prefix.
563
- */
564
- const CLASSNAME$1g = 'lumx-avatar';
565
- const {
566
- block: block$13,
567
- element: element$N
568
- } = classNames.bem(CLASSNAME$1g);
569
-
570
- /**
571
- * Component default props.
572
- */
573
- const DEFAULT_PROPS$17 = {
574
- size: Size$1.m
575
- };
576
-
577
- /**
578
- * Avatar component.
579
- *
580
- * @param props Component props.
581
- * @param ref Component ref.
582
- * @return React element.
583
- */
584
- const Avatar = forwardRef((props, ref) => {
585
- const defaultTheme = useTheme() || Theme$1.light;
586
- const {
587
- actions,
588
- alt,
589
- badge,
590
- className,
591
- image,
592
- linkProps,
593
- linkAs,
594
- onClick,
595
- onKeyPress,
596
- size = DEFAULT_PROPS$17.size,
597
- theme = defaultTheme,
598
- thumbnailProps,
599
- ...forwardedProps
600
- } = props;
601
- return /*#__PURE__*/jsxs("div", {
602
- ref: ref,
603
- ...forwardedProps,
604
- className: classNames.join(className, block$13({
605
- [`size-${size}`]: Boolean(size),
606
- [`theme-${theme}`]: Boolean(theme)
607
- })),
608
- children: [/*#__PURE__*/jsx(Thumbnail, {
609
- linkProps: linkProps,
610
- linkAs: linkAs,
611
- className: element$N('thumbnail'),
612
- onClick: onClick,
613
- onKeyPress: onKeyPress,
614
- ...thumbnailProps,
615
- aspectRatio: AspectRatio$1.square,
616
- size: size,
617
- image: image,
618
- alt: alt,
619
- theme: theme
620
- }), actions && /*#__PURE__*/jsx("div", {
621
- className: element$N('actions'),
622
- children: actions
623
- }), badge && /*#__PURE__*/jsx("div", {
624
- className: element$N('badge'),
625
- children: badge
626
- })]
627
- });
628
- });
629
- Avatar.displayName = COMPONENT_NAME$1h;
630
- Avatar.className = CLASSNAME$1g;
631
- Avatar.defaultProps = DEFAULT_PROPS$17;
632
-
633
- /**
634
- * Alignments.
635
- */
636
- const Theme = {
637
- light: 'light',
638
- dark: 'dark'
639
- };
640
- const Size = {
641
- xxs: 'xxs',
642
- xs: 'xs',
643
- s: 's',
644
- m: 'm',
645
- xl: 'xl',
646
- xxl: 'xxl'};
647
- const Orientation = {
648
- horizontal: 'horizontal'};
649
- const Emphasis = {
650
- low: 'low',
651
- high: 'high'
652
- };
653
- /**
654
- * List of typographies that can't be customized.
655
- */
656
- const TypographyInterface = {
657
- overline: 'overline',
658
- caption: 'caption',
659
- body1: 'body1',
660
- body2: 'body2',
661
- subtitle1: 'subtitle1',
662
- subtitle2: 'subtitle2',
663
- title: 'title',
664
- headline: 'headline',
665
- display1: 'display1'
666
- };
667
- /**
668
- * List of all typographies.
669
- */
670
- const Typography = {
671
- ...TypographyInterface};
672
- /**
673
- * All available aspect ratios.
674
- */
675
- const AspectRatio = {
676
- /** Intrinsic content ratio. */
677
- original: 'original'};
678
- /**
679
- * Semantic info about the purpose of the component
680
- */
681
- const Kind = {
682
- info: 'info',
683
- success: 'success',
684
- warning: 'warning',
685
- error: 'error'
686
- };
687
- /**
688
- * See SCSS variable $lumx-color-palette
689
- */
690
- const ColorPalette = {
691
- primary: 'primary',
692
- blue: 'blue',
693
- dark: 'dark',
694
- green: 'green',
695
- yellow: 'yellow',
696
- red: 'red',
697
- light: 'light'};
698
-
699
556
  function getDefaultExportFromCjs (x) {
700
557
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
701
558
  }
@@ -819,6 +676,12 @@ function typography(typo) {
819
676
  return `lumx-typography-${typo}`;
820
677
  }
821
678
 
679
+ /**
680
+ * Visually hidden class name.
681
+ * Used to hide elements from view but keep them readable from screen readers
682
+ */
683
+ const visuallyHidden = () => VISUALLY_HIDDEN;
684
+
822
685
  /**
823
686
  * Modifier
824
687
  * @example { 'is-disabled': true, 'is-selected': false }
@@ -856,7 +719,7 @@ function modifier$1(baseName, modifiers) {
856
719
  * block('button', { active: true, disabled: false }); // 'button button--active'
857
720
  */
858
721
 
859
- function block$12(baseName, modifiersOrAdditionalClasses, additionalClasses) {
722
+ function block$13(baseName, modifiersOrAdditionalClasses, additionalClasses) {
860
723
  let modifiers;
861
724
  let classes;
862
725
  if (Array.isArray(modifiersOrAdditionalClasses)) {
@@ -891,11 +754,11 @@ function block$12(baseName, modifiersOrAdditionalClasses, additionalClasses) {
891
754
  * element('my-button', 'icon', { active: true }); // 'my-button__icon my-button__icon--active'
892
755
  */
893
756
 
894
- function element$M(baseClass, elem, modifiersOrAdditionalClasses, additionalClasses) {
757
+ function element$N(baseClass, elem, modifiersOrAdditionalClasses, additionalClasses) {
895
758
  if (Array.isArray(modifiersOrAdditionalClasses)) {
896
- return block$12(`${baseClass}__${elem}`, modifiersOrAdditionalClasses);
759
+ return block$13(`${baseClass}__${elem}`, modifiersOrAdditionalClasses);
897
760
  }
898
- return block$12(`${baseClass}__${elem}`, modifiersOrAdditionalClasses, additionalClasses);
761
+ return block$13(`${baseClass}__${elem}`, modifiersOrAdditionalClasses, additionalClasses);
899
762
  }
900
763
 
901
764
  /**
@@ -904,15 +767,15 @@ function element$M(baseClass, elem, modifiersOrAdditionalClasses, additionalClas
904
767
  function bem(baseName) {
905
768
  function blockFn(modifiersOrAdditionalClasses, additionalClasses) {
906
769
  if (Array.isArray(modifiersOrAdditionalClasses)) {
907
- return block$12(baseName, modifiersOrAdditionalClasses);
770
+ return block$13(baseName, modifiersOrAdditionalClasses);
908
771
  }
909
- return block$12(baseName, modifiersOrAdditionalClasses, additionalClasses);
772
+ return block$13(baseName, modifiersOrAdditionalClasses, additionalClasses);
910
773
  }
911
774
  function elementFn(elem, modifiersOrAdditionalClasses, additionalClasses) {
912
775
  if (Array.isArray(modifiersOrAdditionalClasses)) {
913
- return element$M(baseName, elem, modifiersOrAdditionalClasses);
776
+ return element$N(baseName, elem, modifiersOrAdditionalClasses);
914
777
  }
915
- return element$M(baseName, elem, modifiersOrAdditionalClasses, additionalClasses);
778
+ return element$N(baseName, elem, modifiersOrAdditionalClasses, additionalClasses);
916
779
  }
917
780
  return {
918
781
  block: blockFn,
@@ -921,6 +784,207 @@ function bem(baseName) {
921
784
  };
922
785
  }
923
786
 
787
+ /**
788
+ * Animation duration constants. Take into consideration that if you change one of these variables,
789
+ * you need to update their scss counterpart as well
790
+ */
791
+
792
+ /**
793
+ * Delay on hover after which we open or close the tooltip.
794
+ * Only applies to devices supporting pointer hover.
795
+ */
796
+ const TOOLTIP_HOVER_DELAY = {
797
+ open: 500,
798
+ close: 500
799
+ };
800
+
801
+ /**
802
+ * Delay on long press after which we open or close the tooltip.
803
+ * Only applies to devices not supporting pointer hover.
804
+ */
805
+ const TOOLTIP_LONG_PRESS_DELAY = {
806
+ open: 250,
807
+ close: 3000
808
+ };
809
+
810
+ /**
811
+ * Alignments.
812
+ */
813
+ const Theme = {
814
+ light: 'light',
815
+ dark: 'dark'
816
+ };
817
+ const Size = {
818
+ xxs: 'xxs',
819
+ xs: 'xs',
820
+ s: 's',
821
+ m: 'm',
822
+ xl: 'xl',
823
+ xxl: 'xxl'};
824
+ const Orientation = {
825
+ horizontal: 'horizontal'};
826
+ const Emphasis = {
827
+ low: 'low',
828
+ high: 'high'
829
+ };
830
+ /**
831
+ * List of typographies that can't be customized.
832
+ */
833
+ const TypographyInterface = {
834
+ overline: 'overline',
835
+ caption: 'caption',
836
+ body1: 'body1',
837
+ body2: 'body2',
838
+ subtitle1: 'subtitle1',
839
+ subtitle2: 'subtitle2',
840
+ title: 'title',
841
+ headline: 'headline',
842
+ display1: 'display1'
843
+ };
844
+ /**
845
+ * List of all typographies.
846
+ */
847
+ const Typography = {
848
+ ...TypographyInterface};
849
+ /**
850
+ * All available aspect ratios.
851
+ */
852
+ const AspectRatio = {
853
+ /** Intrinsic content ratio. */
854
+ original: 'original'};
855
+ /**
856
+ * Semantic info about the purpose of the component
857
+ */
858
+ const Kind = {
859
+ info: 'info',
860
+ success: 'success',
861
+ warning: 'warning',
862
+ error: 'error'
863
+ };
864
+ /**
865
+ * See SCSS variable $lumx-color-palette
866
+ */
867
+ const ColorPalette = {
868
+ primary: 'primary',
869
+ blue: 'blue',
870
+ dark: 'dark',
871
+ green: 'green',
872
+ yellow: 'yellow',
873
+ red: 'red',
874
+ light: 'light'};
875
+
876
+ /**
877
+ * Check if we are running in a true browser (not SSR and not jsdom test environment).
878
+ */
879
+ const IS_BROWSER = typeof window !== 'undefined' && !window.navigator.userAgent.includes('jsdom');
880
+
881
+ /**
882
+ * Component display name.
883
+ */
884
+ const COMPONENT_NAME$1h = 'Avatar';
885
+
886
+ /**
887
+ * Component default class name and class prefix.
888
+ */
889
+ const CLASSNAME$1g = 'lumx-avatar';
890
+ const {
891
+ block: block$12,
892
+ element: element$M
893
+ } = bem(CLASSNAME$1g);
894
+
895
+ /**
896
+ * Component default props.
897
+ */
898
+ const DEFAULT_PROPS$17 = {
899
+ size: Size.m
900
+ };
901
+
902
+ /**
903
+ * Avatar component.
904
+ *
905
+ * @param props Component props.
906
+ * @param ref Component ref.
907
+ * @return React element.
908
+ */
909
+ const Avatar$1 = props => {
910
+ const {
911
+ actions,
912
+ badge,
913
+ className,
914
+ image,
915
+ size = DEFAULT_PROPS$17.size,
916
+ theme,
917
+ ref,
918
+ ...forwardedProps
919
+ } = props;
920
+ return /*#__PURE__*/jsxs("div", {
921
+ ref: ref,
922
+ ...forwardedProps,
923
+ className: classnames(className, block$12({
924
+ [`size-${size}`]: Boolean(size),
925
+ [`theme-${theme}`]: Boolean(theme)
926
+ })),
927
+ children: [image, actions && /*#__PURE__*/jsx("div", {
928
+ className: element$M('actions'),
929
+ children: actions
930
+ }), badge && /*#__PURE__*/jsx("div", {
931
+ className: element$M('badge'),
932
+ children: badge
933
+ })]
934
+ });
935
+ };
936
+
937
+ /**
938
+ * Avatar component.
939
+ *
940
+ * @param props Component props.
941
+ * @param ref Component ref.
942
+ * @return React element.
943
+ */
944
+ const Avatar = forwardRef((props, ref) => {
945
+ const defaultTheme = useTheme() || Theme$1.light;
946
+ const {
947
+ actions,
948
+ alt,
949
+ badge,
950
+ className,
951
+ image,
952
+ linkProps,
953
+ linkAs,
954
+ onClick,
955
+ onKeyPress,
956
+ size = DEFAULT_PROPS$17.size,
957
+ theme = defaultTheme,
958
+ thumbnailProps,
959
+ ...forwardedProps
960
+ } = props;
961
+ return Avatar$1({
962
+ ...forwardedProps,
963
+ className,
964
+ theme,
965
+ ref,
966
+ actions,
967
+ badge,
968
+ size,
969
+ image: /*#__PURE__*/jsx(Thumbnail, {
970
+ linkProps: linkProps,
971
+ linkAs: linkAs,
972
+ className: element$M('thumbnail'),
973
+ onClick: onClick,
974
+ onKeyPress: onKeyPress,
975
+ ...thumbnailProps,
976
+ aspectRatio: AspectRatio$1.square,
977
+ size: size,
978
+ image: image,
979
+ alt: alt,
980
+ theme: theme
981
+ })
982
+ });
983
+ });
984
+ Avatar.displayName = COMPONENT_NAME$1h;
985
+ Avatar.className = CLASSNAME$1g;
986
+ Avatar.defaultProps = DEFAULT_PROPS$17;
987
+
924
988
  /**
925
989
  * Component display name.
926
990
  */
@@ -2955,21 +3019,6 @@ const DatePickerField = forwardRef((props, ref) => {
2955
3019
  });
2956
3020
  DatePickerField.displayName = COMPONENT_NAME$11;
2957
3021
 
2958
- /**
2959
- * Optional global `window` instance (not defined when running SSR).
2960
- */
2961
- const WINDOW = typeof window !== 'undefined' ? window : undefined;
2962
-
2963
- /**
2964
- * Optional global `document` instance (not defined when running SSR).
2965
- */
2966
- const DOCUMENT = typeof document !== 'undefined' ? document : undefined;
2967
-
2968
- /**
2969
- * Check if we are running in a true browser
2970
- */
2971
- const IS_BROWSER = typeof navigator !== 'undefined' && !navigator.userAgent.includes('jsdom');
2972
-
2973
3022
  /**
2974
3023
  * Keep track of listeners, only the last registered listener gets activated at any point (previously registered
2975
3024
  * listener are disabled).
@@ -6352,7 +6401,7 @@ function usePopoverStyle({
6352
6401
  }
6353
6402
 
6354
6403
  // Arrow middleware
6355
- if (hasArrow) {
6404
+ if (hasArrow && arrowElement) {
6356
6405
  mw.push(arrow({
6357
6406
  element: arrowElement,
6358
6407
  padding: ARROW_SIZE$1 / 2
@@ -6395,10 +6444,6 @@ function usePopoverStyle({
6395
6444
  zIndex
6396
6445
  };
6397
6446
  }, [style, floatingStyles, zIndex]);
6398
-
6399
- // Stable setter wrapping useState setter (already stable but typed for consistency)
6400
- const setPopperElementCallback = useCallback(el => setPopperElement(el), []);
6401
- const setArrowElementCallback = useCallback(el => setArrowElement(el), []);
6402
6447
  return {
6403
6448
  styles: {
6404
6449
  arrow: arrowStyles,
@@ -6406,8 +6451,8 @@ function usePopoverStyle({
6406
6451
  },
6407
6452
  isPositioned,
6408
6453
  position: position,
6409
- setArrowElement: setArrowElementCallback,
6410
- setPopperElement: setPopperElementCallback,
6454
+ setArrowElement,
6455
+ setPopperElement,
6411
6456
  popperElement
6412
6457
  };
6413
6458
  }
@@ -6508,7 +6553,6 @@ const _InnerPopover = forwardRef((props, ref) => {
6508
6553
  [`theme-${theme}`]: Boolean(theme),
6509
6554
  [`elevation-${adjustedElevation}`]: Boolean(adjustedElevation),
6510
6555
  [`position-${position}`]: Boolean(position)
6511
- //'is-initializing': !isPositioned,
6512
6556
  })),
6513
6557
  style: styles.popover,
6514
6558
  "data-popper-placement": position,
@@ -6779,7 +6823,7 @@ const ExpansionPanel = forwardRef((props, ref) => {
6779
6823
  React__default.useEffect(() => {
6780
6824
  if (isOpen || closeMode === 'hide') {
6781
6825
  setChildrenVisible(true);
6782
- } else if (!IS_BROWSER) {
6826
+ } else if (!IS_BROWSER$1) {
6783
6827
  // Outside a browser we can't wait for the transition
6784
6828
  setChildrenVisible(false);
6785
6829
  }
@@ -6791,7 +6835,7 @@ const ExpansionPanel = forwardRef((props, ref) => {
6791
6835
  const {
6792
6836
  current: wrapper
6793
6837
  } = wrapperRef;
6794
- if (!IS_BROWSER || !wrapper) {
6838
+ if (!IS_BROWSER$1 || !wrapper) {
6795
6839
  return undefined;
6796
6840
  }
6797
6841
  const onTransitionEnd = () => {
@@ -14801,10 +14845,90 @@ Toolbar.displayName = TOOLBAR_NAME;
14801
14845
  Toolbar.className = CLASSNAME$3;
14802
14846
  Toolbar.defaultProps = DEFAULT_PROPS$3;
14803
14847
 
14848
+ /**
14849
+ * Arrow size (in pixel).
14850
+ */
14851
+ const ARROW_SIZE = 8;
14852
+
14804
14853
  /**
14805
14854
  * Make sure tooltip appear above popovers.
14855
+ * Hardcoded as POPOVER_ZINDEX (9999) + 1.
14856
+ */
14857
+ const TOOLTIP_ZINDEX = 10000;
14858
+
14859
+ /**
14860
+ * Component default props.
14861
+ */
14862
+ const DEFAULT_PROPS$2 = {
14863
+ placement: 'bottom',
14864
+ closeMode: 'unmount',
14865
+ ariaLinkMode: 'aria-describedby',
14866
+ zIndex: TOOLTIP_ZINDEX
14867
+ };
14868
+
14869
+ /**
14870
+ * Component display name.
14871
+ */
14872
+ const COMPONENT_NAME$2 = 'Tooltip';
14873
+
14874
+ /**
14875
+ * Component default class name and class prefix.
14876
+ */
14877
+ const CLASSNAME$2 = 'lumx-tooltip';
14878
+ const {
14879
+ block: block$2,
14880
+ element: element$2
14881
+ } = bem(CLASSNAME$2);
14882
+
14883
+ /**
14884
+ * Props for the TooltipPopup rendering component.
14806
14885
  */
14807
- const TOOLTIP_ZINDEX = POPOVER_ZINDEX + 1;
14886
+
14887
+ /**
14888
+ * Tooltip popup rendering component.
14889
+ * Pure JSX template for the tooltip popup element (arrow + inner text + BEM classes).
14890
+ *
14891
+ * @param props Component props.
14892
+ * @return JSX element.
14893
+ */
14894
+ const TooltipPopup = props => {
14895
+ const {
14896
+ id,
14897
+ label,
14898
+ position,
14899
+ isHidden,
14900
+ style,
14901
+ zIndex = TOOLTIP_ZINDEX,
14902
+ className,
14903
+ ref,
14904
+ ...forwardedProps
14905
+ } = props;
14906
+ const labelLines = label ? label.split('\n') : [];
14907
+ return /*#__PURE__*/jsxs("div", {
14908
+ ref: ref,
14909
+ ...forwardedProps,
14910
+ id: id,
14911
+ role: "tooltip",
14912
+ className: classnames(className, block$2({
14913
+ [`position-${position}`]: Boolean(position)
14914
+ }), isHidden && visuallyHidden()),
14915
+ style: {
14916
+ ...(isHidden ? undefined : style),
14917
+ zIndex
14918
+ },
14919
+ "data-popper-placement": position,
14920
+ children: [/*#__PURE__*/jsx("div", {
14921
+ className: element$2('arrow')
14922
+ }), /*#__PURE__*/jsx("div", {
14923
+ className: element$2('inner'),
14924
+ children: labelLines.map((line, index) => /*#__PURE__*/jsx("p", {
14925
+ children: line
14926
+ }, index))
14927
+ })]
14928
+ });
14929
+ };
14930
+ TooltipPopup.displayName = COMPONENT_NAME$2;
14931
+ TooltipPopup.className = CLASSNAME$2;
14808
14932
 
14809
14933
  /**
14810
14934
  * Add ref and ARIA attribute(s) in tooltip children or wrapped children.
@@ -14852,9 +14976,6 @@ const useInjectTooltipRef = options => {
14852
14976
  }, [label, children, setAnchorElement, linkId, ariaLinkMode]);
14853
14977
  };
14854
14978
 
14855
- /** Return true if the browser does not support pointer hover */
14856
- const isHoverNotSupported = () => !!window.matchMedia?.('(hover: none)').matches;
14857
-
14858
14979
  /** Check if the focus is visible on the given element */
14859
14980
  const isFocusVisible = element => {
14860
14981
  try {
@@ -14865,6 +14986,140 @@ const isFocusVisible = element => {
14865
14986
  }
14866
14987
  };
14867
14988
 
14989
+ /** Return true if the browser does not support pointer hover */
14990
+ const isHoverNotSupported = () => !!window.matchMedia?.('(hover: none)').matches;
14991
+
14992
+ /**
14993
+ * Framework-agnostic open/close state machine for tooltip.
14994
+ * Manages hover, touch, focus, timers, and escape key behavior.
14995
+ */
14996
+ function createTooltipOpenManager(options) {
14997
+ const {
14998
+ delay,
14999
+ onStateChange
15000
+ } = options;
15001
+ let timer;
15002
+ let openStartTime;
15003
+ let shouldOpen;
15004
+ let anchorController;
15005
+ let popperController;
15006
+
15007
+ // Run timer to defer updating the isOpen state.
15008
+ const deferUpdate = duration => {
15009
+ if (timer) clearTimeout(timer);
15010
+ const update = () => {
15011
+ onStateChange(!!shouldOpen);
15012
+ };
15013
+ // Skip timeout in fake browsers
15014
+ if (!IS_BROWSER) update();else timer = setTimeout(update, duration);
15015
+ };
15016
+ const hoverNotSupported = isHoverNotSupported();
15017
+ const hasTouch = typeof window !== 'undefined' && 'ontouchstart' in window;
15018
+
15019
+ // Adapt open/close delay
15020
+ const openDelay = delay || (hoverNotSupported ? TOOLTIP_LONG_PRESS_DELAY.open : TOOLTIP_HOVER_DELAY.open);
15021
+ const closeDelay = hoverNotSupported ? TOOLTIP_LONG_PRESS_DELAY.close : TOOLTIP_HOVER_DELAY.close;
15022
+
15023
+ // Open (or/and cancel closing) of tooltip.
15024
+ const open = () => {
15025
+ if (shouldOpen && !timer) return;
15026
+ shouldOpen = true;
15027
+ openStartTime = Date.now();
15028
+ deferUpdate(openDelay);
15029
+ };
15030
+
15031
+ // Close or cancel opening of tooltip
15032
+ const getClose = overrideDelay => {
15033
+ if (!shouldOpen && !timer) return;
15034
+ shouldOpen = false;
15035
+ deferUpdate(overrideDelay);
15036
+ };
15037
+ const close = () => getClose(closeDelay);
15038
+ const closeImmediately = () => getClose(0);
15039
+ return {
15040
+ attachAnchor(anchorElement) {
15041
+ anchorController = new AbortController();
15042
+ const {
15043
+ signal
15044
+ } = anchorController;
15045
+ if (hoverNotSupported) {
15046
+ /**
15047
+ * Handle touchend event.
15048
+ * If end comes before the open delay => cancel tooltip (close immediate).
15049
+ * Else if end comes after the open delay => tooltip takes priority, the anchor's default touch end event is prevented.
15050
+ */
15051
+ const longPressEnd = evt => {
15052
+ if (!openStartTime) return;
15053
+ if (Date.now() - openStartTime >= openDelay) {
15054
+ // Tooltip take priority, event prevented.
15055
+ evt.stopPropagation();
15056
+ evt.preventDefault();
15057
+ anchorElement.focus();
15058
+ // Close with delay.
15059
+ close();
15060
+ } else {
15061
+ // Close immediately.
15062
+ closeImmediately();
15063
+ }
15064
+ };
15065
+ anchorElement.addEventListener(hasTouch ? 'touchstart' : 'mousedown', open, {
15066
+ signal
15067
+ });
15068
+ anchorElement.addEventListener(hasTouch ? 'touchend' : 'mouseup', longPressEnd, {
15069
+ signal
15070
+ });
15071
+ } else {
15072
+ anchorElement.addEventListener('mouseenter', open, {
15073
+ signal
15074
+ });
15075
+ anchorElement.addEventListener('mouseleave', close, {
15076
+ signal
15077
+ });
15078
+ anchorElement.addEventListener('mouseup', closeImmediately, {
15079
+ signal
15080
+ });
15081
+ }
15082
+
15083
+ // Events always applied no matter the browser:
15084
+ // Open on focus (only if focus is visible).
15085
+ anchorElement.addEventListener('focusin', e => {
15086
+ // Skip if focus is not visible
15087
+ if (!isFocusVisible(e.target)) return;
15088
+ open();
15089
+ }, {
15090
+ signal
15091
+ });
15092
+ // Close on lost focus.
15093
+ anchorElement.addEventListener('focusout', closeImmediately, {
15094
+ signal
15095
+ });
15096
+ },
15097
+ attachPopper(popperElement) {
15098
+ popperController?.abort();
15099
+ if (!popperElement || hoverNotSupported) return;
15100
+ popperController = new AbortController();
15101
+ const {
15102
+ signal
15103
+ } = popperController;
15104
+ // Popper element hover
15105
+ popperElement.addEventListener('mouseenter', open, {
15106
+ signal
15107
+ });
15108
+ popperElement.addEventListener('mouseleave', close, {
15109
+ signal
15110
+ });
15111
+ },
15112
+ close() {
15113
+ closeImmediately();
15114
+ },
15115
+ destroy() {
15116
+ if (timer) clearTimeout(timer);
15117
+ anchorController?.abort();
15118
+ popperController?.abort();
15119
+ }
15120
+ };
15121
+ }
15122
+
14868
15123
  /**
14869
15124
  * Hook controlling tooltip visibility using mouse hover the anchor and delay.
14870
15125
  *
@@ -14875,110 +15130,23 @@ const isFocusVisible = element => {
14875
15130
  function useTooltipOpen(delay, anchorElement) {
14876
15131
  const [isOpen, setIsOpen] = useState(false);
14877
15132
  const onPopperMount = useRef(null);
15133
+ const managerRef = useRef(null);
14878
15134
 
14879
15135
  // Global close on escape
14880
- const [closeCallback, setCloseCallback] = useState(undefined);
14881
- useCallbackOnEscape(isOpen ? closeCallback : undefined);
15136
+ useCallbackOnEscape(isOpen ? () => managerRef.current?.close() : undefined);
14882
15137
  useEffect(() => {
14883
15138
  if (!anchorElement) {
14884
15139
  return undefined;
14885
15140
  }
14886
- let timer;
14887
- let openStartTime;
14888
- let shouldOpen;
14889
-
14890
- // Run timer to defer updating the isOpen state.
14891
- const deferUpdate = duration => {
14892
- if (timer) clearTimeout(timer);
14893
- const update = () => {
14894
- setIsOpen(!!shouldOpen);
14895
- };
14896
- // Skip timeout in fake browsers
14897
- if (!IS_BROWSER) update();else timer = setTimeout(update, duration);
14898
- };
14899
- const hoverNotSupported = isHoverNotSupported();
14900
- const hasTouch = 'ontouchstart' in window;
14901
-
14902
- // Adapt open/close delay
14903
- const openDelay = delay || (hoverNotSupported ? TOOLTIP_LONG_PRESS_DELAY.open : TOOLTIP_HOVER_DELAY.open);
14904
- const closeDelay = hoverNotSupported ? TOOLTIP_LONG_PRESS_DELAY.close : TOOLTIP_HOVER_DELAY.close;
14905
-
14906
- // Open (or/and cancel closing) of tooltip.
14907
- const open = () => {
14908
- if (shouldOpen && !timer) return;
14909
- shouldOpen = true;
14910
- openStartTime = Date.now();
14911
- deferUpdate(openDelay);
14912
- };
14913
-
14914
- // Close or cancel opening of tooltip
14915
- const getClose = (overrideDelay = closeDelay) => {
14916
- if (!shouldOpen && !timer) return;
14917
- shouldOpen = false;
14918
- deferUpdate(overrideDelay);
14919
- };
14920
- const close = () => getClose(closeDelay);
14921
- const closeImmediately = () => getClose(0);
14922
- setCloseCallback(() => closeImmediately);
14923
-
14924
- // Adapt event to browsers with or without `hover` support.
14925
- const events = [];
14926
- if (hoverNotSupported) {
14927
- /**
14928
- * Handle touchend event
14929
- * If end comes before the open delay => cancel tooltip (close immediate).
14930
- * Else if end comes after the open delay => tooltip takes priority, the anchor's default touch end event is prevented.
14931
- */
14932
- const longPressEnd = evt => {
14933
- if (!openStartTime) return;
14934
- if (Date.now() - openStartTime >= openDelay) {
14935
- // Tooltip take priority, event prevented.
14936
- evt.stopPropagation();
14937
- evt.preventDefault();
14938
- anchorElement.focus();
14939
- // Close with delay.
14940
- close();
14941
- } else {
14942
- // Close immediately.
14943
- closeImmediately();
14944
- }
14945
- };
14946
- events.push([anchorElement, hasTouch ? 'touchstart' : 'mousedown', open], [anchorElement, hasTouch ? 'touchend' : 'mouseup', longPressEnd]);
14947
- } else {
14948
- events.push([anchorElement, 'mouseenter', open], [anchorElement, 'mouseleave', close], [anchorElement, 'mouseup', closeImmediately]);
14949
- onPopperMount.current = popperElement => {
14950
- if (!popperElement) return;
14951
- // Popper element hover
14952
- popperElement.addEventListener('mouseenter', open);
14953
- popperElement.addEventListener('mouseleave', close);
14954
- // Add to event list to remove on unmount
14955
- events.push([popperElement, 'mouseenter', open], [popperElement, 'mouseleave', close]);
14956
- };
14957
- }
14958
-
14959
- // Events always applied no matter the browser:.
14960
- events.push(
14961
- // Open on focus (only if focus is visible).
14962
- [anchorElement, 'focusin', e => {
14963
- // Skip if focus is not visible
14964
- if (!isFocusVisible(e.target)) return;
14965
- open();
14966
- }],
14967
- // Close on lost focus.
14968
- [anchorElement, 'focusout', closeImmediately]);
14969
-
14970
- // Attach events
14971
- for (const [node, eventType, eventHandler] of events) {
14972
- node.addEventListener(eventType, eventHandler);
14973
- }
15141
+ const manager = createTooltipOpenManager({
15142
+ delay,
15143
+ onStateChange: setIsOpen
15144
+ });
15145
+ managerRef.current = manager;
15146
+ onPopperMount.current = el => manager.attachPopper(el);
15147
+ manager.attachAnchor(anchorElement);
14974
15148
  return () => {
14975
- // Clear pending timers.
14976
- if (timer) clearTimeout(timer);
14977
-
14978
- // Detach events.
14979
- for (const [node, eventType, eventHandler] of events) {
14980
- node.removeEventListener(eventType, eventHandler);
14981
- }
15149
+ manager.destroy();
14982
15150
  };
14983
15151
  }, [anchorElement, delay]);
14984
15152
  return {
@@ -14988,35 +15156,6 @@ function useTooltipOpen(delay, anchorElement) {
14988
15156
  }
14989
15157
 
14990
15158
  /* eslint-disable react-hooks/rules-of-hooks */
14991
- /**
14992
- * Component display name.
14993
- */
14994
- const COMPONENT_NAME$2 = 'Tooltip';
14995
-
14996
- /**
14997
- * Component default class name and class prefix.
14998
- */
14999
- const CLASSNAME$2 = 'lumx-tooltip';
15000
- const {
15001
- block: block$2,
15002
- element: element$2
15003
- } = classNames.bem(CLASSNAME$2);
15004
-
15005
- /**
15006
- * Component default props.
15007
- */
15008
- const DEFAULT_PROPS$2 = {
15009
- placement: Placement.BOTTOM,
15010
- closeMode: 'unmount',
15011
- ariaLinkMode: 'aria-describedby',
15012
- zIndex: TOOLTIP_ZINDEX
15013
- };
15014
-
15015
- /**
15016
- * Arrow size (in pixel).
15017
- */
15018
- const ARROW_SIZE = 8;
15019
-
15020
15159
  /**
15021
15160
  * Tooltip component.
15022
15161
  *
@@ -15048,9 +15187,7 @@ const Tooltip = forwardRef((props, ref) => {
15048
15187
  const [anchorElement, setAnchorElement] = useState(null);
15049
15188
  const {
15050
15189
  floatingStyles,
15051
- placement: resolvedPlacement,
15052
- isPositioned,
15053
- update
15190
+ placement: resolvedPlacement
15054
15191
  } = useFloating({
15055
15192
  placement: placement,
15056
15193
  whileElementsMounted: autoUpdate,
@@ -15076,40 +15213,21 @@ const Tooltip = forwardRef((props, ref) => {
15076
15213
  label,
15077
15214
  ariaLinkMode: ariaLinkMode
15078
15215
  });
15079
-
15080
- // Update on open
15081
- // React.useEffect(() => {
15082
- // if (isOpen || popperElement) update?.();
15083
- // }, [isOpen, update, popperElement]);
15084
-
15085
- const labelLines = label ? label.split('\n') : [];
15086
15216
  const tooltipRef = useMergeRefs(ref, setPopperElement, onPopperMount);
15087
15217
  return /*#__PURE__*/jsxs(Fragment, {
15088
15218
  children: [/*#__PURE__*/jsx(TooltipContextProvider, {
15089
15219
  children: wrappedChildren
15090
15220
  }), isMounted && /*#__PURE__*/jsx(Portal, {
15091
- children: /*#__PURE__*/jsxs("div", {
15221
+ children: TooltipPopup({
15092
15222
  ref: tooltipRef,
15093
15223
  ...forwardedProps,
15094
- id: id,
15095
- role: "tooltip",
15096
- className: classNames.join(className, block$2({
15097
- [`position-${position}`]: Boolean(position)
15098
- //'is-initializing': !isPositioned,
15099
- }), isHidden && classNames.visuallyHidden()),
15100
- style: {
15101
- ...(isHidden ? undefined : floatingStyles),
15102
- zIndex
15103
- },
15104
- "data-popper-placement": position,
15105
- children: [/*#__PURE__*/jsx("div", {
15106
- className: element$2('arrow')
15107
- }), /*#__PURE__*/jsx("div", {
15108
- className: element$2('inner'),
15109
- children: labelLines.map(line => /*#__PURE__*/jsx("p", {
15110
- children: line
15111
- }, line))
15112
- })]
15224
+ id,
15225
+ label: label,
15226
+ position: position,
15227
+ isHidden,
15228
+ style: isHidden ? undefined : floatingStyles,
15229
+ zIndex,
15230
+ className
15113
15231
  })
15114
15232
  })]
15115
15233
  });