useTarget.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import { computed, watchEffect, onMounted, watch, onBeforeUnmount } from 'vue';
  2. import { isInViewPort } from '../util';
  3. import useState from '../../_util/hooks/useState';
  4. export default function useTarget(target, open, gap, scrollIntoViewOptions) {
  5. // ========================= Target =========================
  6. // We trade `undefined` as not get target by function yet.
  7. // `null` as empty target.
  8. const [targetElement, setTargetElement] = useState(undefined);
  9. watchEffect(() => {
  10. const nextElement = typeof target.value === 'function' ? target.value() : target.value;
  11. setTargetElement(nextElement || null);
  12. }, {
  13. flush: 'post'
  14. });
  15. // ========================= Align ==========================
  16. const [posInfo, setPosInfo] = useState(null);
  17. const updatePos = () => {
  18. if (!open.value) {
  19. setPosInfo(null);
  20. return;
  21. }
  22. if (targetElement.value) {
  23. // Exist target element. We should scroll and get target position
  24. if (!isInViewPort(targetElement.value) && open.value) {
  25. targetElement.value.scrollIntoView(scrollIntoViewOptions.value);
  26. }
  27. const {
  28. left,
  29. top,
  30. width,
  31. height
  32. } = targetElement.value.getBoundingClientRect();
  33. const nextPosInfo = {
  34. left,
  35. top,
  36. width,
  37. height,
  38. radius: 0
  39. };
  40. if (JSON.stringify(posInfo.value) !== JSON.stringify(nextPosInfo)) {
  41. setPosInfo(nextPosInfo);
  42. }
  43. } else {
  44. // Not exist target which means we just show in center
  45. setPosInfo(null);
  46. }
  47. };
  48. onMounted(() => {
  49. watch([open, targetElement], () => {
  50. updatePos();
  51. }, {
  52. flush: 'post',
  53. immediate: true
  54. });
  55. // update when window resize
  56. window.addEventListener('resize', updatePos);
  57. });
  58. onBeforeUnmount(() => {
  59. window.removeEventListener('resize', updatePos);
  60. });
  61. // ======================== PosInfo =========================
  62. const mergedPosInfo = computed(() => {
  63. var _a, _b;
  64. if (!posInfo.value) {
  65. return posInfo.value;
  66. }
  67. const gapOffset = ((_a = gap.value) === null || _a === void 0 ? void 0 : _a.offset) || 6;
  68. const gapRadius = ((_b = gap.value) === null || _b === void 0 ? void 0 : _b.radius) || 2;
  69. return {
  70. left: posInfo.value.left - gapOffset,
  71. top: posInfo.value.top - gapOffset,
  72. width: posInfo.value.width + gapOffset * 2,
  73. height: posInfo.value.height + gapOffset * 2,
  74. radius: gapRadius
  75. };
  76. });
  77. return [mergedPosInfo, targetElement];
  78. }