Source

adminjs-design-system/src/atoms/tooltip/tooltip-control.tsx

  1. import React, { useEffect, useRef, useState } from 'react'
  2. import useWindowSize from '../../hooks/use-window-size'
  3. import { PortalProps } from './tooltip-props'
  4. import { StyledTooltip } from './tooltip-styled'
  5. type PositionProps = {
  6. top: number;
  7. left: number;
  8. width: number;
  9. height: number;
  10. }
  11. /**
  12. * @component
  13. * @private
  14. * @memberof Tooltip
  15. */
  16. const TooltipControl: React.FC<PortalProps> = (props) => {
  17. const { title, childRef, direction = 'bottom', ContentElement, size } = props
  18. const tooltipRef = useRef<HTMLElement>(null)
  19. const [dimension, setDimension] = useState<Pick<PositionProps, 'width' | 'height'> | null>(null)
  20. const [position, setPosition] = useState<Pick<PositionProps, 'left' | 'top'> | null>(null)
  21. const [elementPosition, setElementPosition] = useState<PositionProps | null>(null)
  22. const windowSize = useWindowSize()
  23. useEffect(() => {
  24. if (childRef.current) {
  25. const {
  26. width: clientWidth,
  27. top: offsetTop,
  28. left: offsetLeft,
  29. height: clientHeight,
  30. } = childRef.current.getBoundingClientRect()
  31. setElementPosition({
  32. width: clientWidth,
  33. top: offsetTop,
  34. left: offsetLeft,
  35. height: clientHeight,
  36. })
  37. }
  38. }, [
  39. childRef,
  40. windowSize?.width,
  41. windowSize?.height,
  42. ])
  43. useEffect(() => {
  44. if (tooltipRef.current) {
  45. const { clientWidth, clientHeight } = tooltipRef.current
  46. setDimension({
  47. width: clientWidth,
  48. height: clientHeight,
  49. })
  50. }
  51. }, [
  52. tooltipRef?.current?.clientWidth,
  53. tooltipRef?.current?.clientHeight,
  54. title,
  55. ])
  56. useEffect(() => {
  57. if (!elementPosition || !dimension) {
  58. return
  59. }
  60. // eslint-disable-next-line default-case
  61. switch (direction) {
  62. case 'bottom': {
  63. setPosition({
  64. top: elementPosition.top + elementPosition.height,
  65. left: elementPosition.left + elementPosition.width / 2 - dimension.width / 2,
  66. })
  67. break
  68. }
  69. case 'top': {
  70. setPosition({
  71. top: elementPosition.top - dimension.height,
  72. left: elementPosition.left + elementPosition.width / 2 - dimension.width / 2,
  73. })
  74. break
  75. }
  76. case 'left': {
  77. setPosition({
  78. top: elementPosition.top + elementPosition.height / 2 - dimension.height / 2,
  79. left: elementPosition.left - dimension.width,
  80. })
  81. break
  82. }
  83. case 'right': {
  84. setPosition({
  85. top: elementPosition.top + elementPosition.height / 2 - dimension.height / 2,
  86. left: elementPosition.left + elementPosition.width,
  87. })
  88. break
  89. }
  90. }
  91. }, [elementPosition, dimension, direction])
  92. const isVisible = !!(dimension && position)
  93. return (
  94. <StyledTooltip
  95. ref={tooltipRef}
  96. left={position?.left || '-1110px'}
  97. top={position?.top || '-1110px'}
  98. size={size}
  99. direction={direction}
  100. isVisible={isVisible}
  101. >
  102. { ContentElement || title }
  103. </StyledTooltip>
  104. )
  105. }
  106. export {
  107. TooltipControl as default,
  108. TooltipControl,
  109. }