@thoughtspot/visual-embed-sdk 1.48.0 → 1.49.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 (217) hide show
  1. package/cjs/package.json +1 -1
  2. package/cjs/src/css-variables.d.ts +140 -0
  3. package/cjs/src/css-variables.d.ts.map +1 -1
  4. package/cjs/src/embed/app.d.ts +62 -1
  5. package/cjs/src/embed/app.d.ts.map +1 -1
  6. package/cjs/src/embed/app.js +57 -6
  7. package/cjs/src/embed/app.js.map +1 -1
  8. package/cjs/src/embed/app.spec.js +191 -1
  9. package/cjs/src/embed/app.spec.js.map +1 -1
  10. package/cjs/src/embed/auto-frame-renderer.js +7 -2
  11. package/cjs/src/embed/auto-frame-renderer.js.map +1 -1
  12. package/cjs/src/embed/auto-frame-renderer.spec.js +385 -6
  13. package/cjs/src/embed/auto-frame-renderer.spec.js.map +1 -1
  14. package/cjs/src/embed/base.d.ts +1 -0
  15. package/cjs/src/embed/base.d.ts.map +1 -1
  16. package/cjs/src/embed/base.js +13 -1
  17. package/cjs/src/embed/base.js.map +1 -1
  18. package/cjs/src/embed/base.spec.js +21 -0
  19. package/cjs/src/embed/base.spec.js.map +1 -1
  20. package/cjs/src/embed/bodyless-conversation.spec.js +86 -0
  21. package/cjs/src/embed/bodyless-conversation.spec.js.map +1 -1
  22. package/cjs/src/embed/conversation.d.ts +16 -1
  23. package/cjs/src/embed/conversation.d.ts.map +1 -1
  24. package/cjs/src/embed/conversation.js +5 -1
  25. package/cjs/src/embed/conversation.js.map +1 -1
  26. package/cjs/src/embed/conversation.spec.js +26 -0
  27. package/cjs/src/embed/conversation.spec.js.map +1 -1
  28. package/cjs/src/embed/liveboard.d.ts +47 -1
  29. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  30. package/cjs/src/embed/liveboard.js +47 -6
  31. package/cjs/src/embed/liveboard.js.map +1 -1
  32. package/cjs/src/embed/liveboard.spec.js +129 -1
  33. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  34. package/cjs/src/embed/spotter-viz-utils.d.ts +85 -0
  35. package/cjs/src/embed/spotter-viz-utils.d.ts.map +1 -0
  36. package/cjs/src/embed/spotter-viz-utils.js +17 -0
  37. package/cjs/src/embed/spotter-viz-utils.js.map +1 -0
  38. package/cjs/src/embed/spotter-viz-utils.spec.d.ts +2 -0
  39. package/cjs/src/embed/spotter-viz-utils.spec.d.ts.map +1 -0
  40. package/cjs/src/embed/spotter-viz-utils.spec.js +31 -0
  41. package/cjs/src/embed/spotter-viz-utils.spec.js.map +1 -0
  42. package/cjs/src/embed/ts-embed.d.ts +58 -38
  43. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  44. package/cjs/src/embed/ts-embed.js +247 -151
  45. package/cjs/src/embed/ts-embed.js.map +1 -1
  46. package/cjs/src/embed/ts-embed.spec.js +369 -123
  47. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  48. package/cjs/src/index.d.ts +2 -1
  49. package/cjs/src/index.d.ts.map +1 -1
  50. package/cjs/src/index.js.map +1 -1
  51. package/cjs/src/mixpanel-service.js +2 -2
  52. package/cjs/src/mixpanel-service.js.map +1 -1
  53. package/cjs/src/react/index.d.ts.map +1 -1
  54. package/cjs/src/react/index.js +3 -0
  55. package/cjs/src/react/index.js.map +1 -1
  56. package/cjs/src/types.d.ts +267 -27
  57. package/cjs/src/types.d.ts.map +1 -1
  58. package/cjs/src/types.js +223 -19
  59. package/cjs/src/types.js.map +1 -1
  60. package/cjs/src/utils/authService/tokenizedAuthService.spec.js +6 -7
  61. package/cjs/src/utils/authService/tokenizedAuthService.spec.js.map +1 -1
  62. package/cjs/src/utils/logger.js +2 -1
  63. package/cjs/src/utils/logger.js.map +1 -1
  64. package/cjs/src/utils/logger.spec.d.ts +1 -0
  65. package/cjs/src/utils/logger.spec.d.ts.map +1 -1
  66. package/cjs/src/utils/logger.spec.js +10 -9
  67. package/cjs/src/utils/logger.spec.js.map +1 -1
  68. package/cjs/src/utils/sdk-version.d.ts +2 -0
  69. package/cjs/src/utils/sdk-version.d.ts.map +1 -0
  70. package/cjs/src/utils/sdk-version.js +7 -0
  71. package/cjs/src/utils/sdk-version.js.map +1 -0
  72. package/cjs/src/utils.d.ts +4 -1
  73. package/cjs/src/utils.d.ts.map +1 -1
  74. package/cjs/src/utils.js +107 -10
  75. package/cjs/src/utils.js.map +1 -1
  76. package/cjs/src/utils.spec.js +163 -4
  77. package/cjs/src/utils.spec.js.map +1 -1
  78. package/dist/{index-Ck-r09gt.js → index-B6Rn561t.js} +1 -1
  79. package/dist/src/css-variables.d.ts +140 -0
  80. package/dist/src/css-variables.d.ts.map +1 -1
  81. package/dist/src/embed/app.d.ts +62 -1
  82. package/dist/src/embed/app.d.ts.map +1 -1
  83. package/dist/src/embed/base.d.ts +1 -0
  84. package/dist/src/embed/base.d.ts.map +1 -1
  85. package/dist/src/embed/conversation.d.ts +16 -1
  86. package/dist/src/embed/conversation.d.ts.map +1 -1
  87. package/dist/src/embed/liveboard.d.ts +47 -1
  88. package/dist/src/embed/liveboard.d.ts.map +1 -1
  89. package/dist/src/embed/spotter-viz-utils.d.ts +85 -0
  90. package/dist/src/embed/spotter-viz-utils.d.ts.map +1 -0
  91. package/dist/src/embed/spotter-viz-utils.spec.d.ts +2 -0
  92. package/dist/src/embed/spotter-viz-utils.spec.d.ts.map +1 -0
  93. package/dist/src/embed/ts-embed.d.ts +58 -38
  94. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  95. package/dist/src/index.d.ts +2 -1
  96. package/dist/src/index.d.ts.map +1 -1
  97. package/dist/src/react/index.d.ts.map +1 -1
  98. package/dist/src/types.d.ts +267 -27
  99. package/dist/src/types.d.ts.map +1 -1
  100. package/dist/src/utils/logger.spec.d.ts +1 -0
  101. package/dist/src/utils/logger.spec.d.ts.map +1 -1
  102. package/dist/src/utils/sdk-version.d.ts +2 -0
  103. package/dist/src/utils/sdk-version.d.ts.map +1 -0
  104. package/dist/src/utils.d.ts +4 -1
  105. package/dist/src/utils.d.ts.map +1 -1
  106. package/dist/tsembed-react.es.js +3710 -3226
  107. package/dist/tsembed-react.js +3360 -2876
  108. package/dist/tsembed.es.js +3715 -3229
  109. package/dist/tsembed.js +3710 -3224
  110. package/dist/visual-embed-sdk-react-full.d.ts +643 -63
  111. package/dist/visual-embed-sdk-react.d.ts +643 -63
  112. package/dist/visual-embed-sdk.d.ts +658 -65
  113. package/lib/package.json +1 -1
  114. package/lib/src/css-variables.d.ts +140 -0
  115. package/lib/src/css-variables.d.ts.map +1 -1
  116. package/lib/src/embed/app.d.ts +62 -1
  117. package/lib/src/embed/app.d.ts.map +1 -1
  118. package/lib/src/embed/app.js +58 -7
  119. package/lib/src/embed/app.js.map +1 -1
  120. package/lib/src/embed/app.spec.js +192 -2
  121. package/lib/src/embed/app.spec.js.map +1 -1
  122. package/lib/src/embed/auto-frame-renderer.js +7 -2
  123. package/lib/src/embed/auto-frame-renderer.js.map +1 -1
  124. package/lib/src/embed/auto-frame-renderer.spec.js +387 -8
  125. package/lib/src/embed/auto-frame-renderer.spec.js.map +1 -1
  126. package/lib/src/embed/base.d.ts +1 -0
  127. package/lib/src/embed/base.d.ts.map +1 -1
  128. package/lib/src/embed/base.js +11 -0
  129. package/lib/src/embed/base.js.map +1 -1
  130. package/lib/src/embed/base.spec.js +22 -1
  131. package/lib/src/embed/base.spec.js.map +1 -1
  132. package/lib/src/embed/bodyless-conversation.spec.js +86 -0
  133. package/lib/src/embed/bodyless-conversation.spec.js.map +1 -1
  134. package/lib/src/embed/conversation.d.ts +16 -1
  135. package/lib/src/embed/conversation.d.ts.map +1 -1
  136. package/lib/src/embed/conversation.js +5 -1
  137. package/lib/src/embed/conversation.js.map +1 -1
  138. package/lib/src/embed/conversation.spec.js +27 -1
  139. package/lib/src/embed/conversation.spec.js.map +1 -1
  140. package/lib/src/embed/liveboard.d.ts +47 -1
  141. package/lib/src/embed/liveboard.d.ts.map +1 -1
  142. package/lib/src/embed/liveboard.js +48 -7
  143. package/lib/src/embed/liveboard.js.map +1 -1
  144. package/lib/src/embed/liveboard.spec.js +129 -1
  145. package/lib/src/embed/liveboard.spec.js.map +1 -1
  146. package/lib/src/embed/spotter-viz-utils.d.ts +85 -0
  147. package/lib/src/embed/spotter-viz-utils.d.ts.map +1 -0
  148. package/lib/src/embed/spotter-viz-utils.js +13 -0
  149. package/lib/src/embed/spotter-viz-utils.js.map +1 -0
  150. package/lib/src/embed/spotter-viz-utils.spec.d.ts +2 -0
  151. package/lib/src/embed/spotter-viz-utils.spec.d.ts.map +1 -0
  152. package/lib/src/embed/spotter-viz-utils.spec.js +29 -0
  153. package/lib/src/embed/spotter-viz-utils.spec.js.map +1 -0
  154. package/lib/src/embed/ts-embed.d.ts +58 -38
  155. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  156. package/lib/src/embed/ts-embed.js +249 -153
  157. package/lib/src/embed/ts-embed.js.map +1 -1
  158. package/lib/src/embed/ts-embed.spec.js +369 -123
  159. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  160. package/lib/src/index.d.ts +2 -1
  161. package/lib/src/index.d.ts.map +1 -1
  162. package/lib/src/index.js.map +1 -1
  163. package/lib/src/mixpanel-service.js +1 -1
  164. package/lib/src/mixpanel-service.js.map +1 -1
  165. package/lib/src/react/index.d.ts.map +1 -1
  166. package/lib/src/react/index.js +3 -0
  167. package/lib/src/react/index.js.map +1 -1
  168. package/lib/src/types.d.ts +267 -27
  169. package/lib/src/types.d.ts.map +1 -1
  170. package/lib/src/types.js +223 -19
  171. package/lib/src/types.js.map +1 -1
  172. package/lib/src/utils/authService/tokenizedAuthService.spec.js +6 -7
  173. package/lib/src/utils/authService/tokenizedAuthService.spec.js.map +1 -1
  174. package/lib/src/utils/logger.js +2 -1
  175. package/lib/src/utils/logger.js.map +1 -1
  176. package/lib/src/utils/logger.spec.d.ts +1 -0
  177. package/lib/src/utils/logger.spec.d.ts.map +1 -1
  178. package/lib/src/utils/logger.spec.js +10 -9
  179. package/lib/src/utils/logger.spec.js.map +1 -1
  180. package/lib/src/utils/sdk-version.d.ts +2 -0
  181. package/lib/src/utils/sdk-version.d.ts.map +1 -0
  182. package/lib/src/utils/sdk-version.js +3 -0
  183. package/lib/src/utils/sdk-version.js.map +1 -0
  184. package/lib/src/utils.d.ts +4 -1
  185. package/lib/src/utils.d.ts.map +1 -1
  186. package/lib/src/utils.js +103 -9
  187. package/lib/src/utils.js.map +1 -1
  188. package/lib/src/utils.spec.js +164 -5
  189. package/lib/src/utils.spec.js.map +1 -1
  190. package/lib/src/visual-embed-sdk.d.ts +658 -65
  191. package/package.json +1 -1
  192. package/src/css-variables.ts +175 -1
  193. package/src/embed/app.spec.ts +247 -3
  194. package/src/embed/app.ts +125 -5
  195. package/src/embed/auto-frame-renderer.spec.ts +457 -58
  196. package/src/embed/auto-frame-renderer.ts +7 -2
  197. package/src/embed/base.spec.ts +25 -1
  198. package/src/embed/base.ts +19 -5
  199. package/src/embed/bodyless-conversation.spec.ts +93 -0
  200. package/src/embed/conversation.spec.ts +34 -0
  201. package/src/embed/conversation.ts +22 -1
  202. package/src/embed/liveboard.spec.ts +149 -1
  203. package/src/embed/liveboard.ts +102 -6
  204. package/src/embed/spotter-viz-utils.spec.ts +30 -0
  205. package/src/embed/spotter-viz-utils.ts +94 -0
  206. package/src/embed/ts-embed.spec.ts +532 -234
  207. package/src/embed/ts-embed.ts +384 -258
  208. package/src/index.ts +3 -0
  209. package/src/mixpanel-service.ts +1 -1
  210. package/src/react/index.tsx +3 -0
  211. package/src/types.ts +284 -23
  212. package/src/utils/authService/tokenizedAuthService.spec.ts +6 -6
  213. package/src/utils/logger.spec.ts +11 -9
  214. package/src/utils/logger.ts +2 -2
  215. package/src/utils/sdk-version.ts +3 -0
  216. package/src/utils.spec.ts +200 -4
  217. package/src/utils.ts +128 -9
package/src/utils.spec.ts CHANGED
@@ -18,6 +18,9 @@ import {
18
18
  getTypeFromValue,
19
19
  arrayIncludesString,
20
20
  calculateVisibleElementData,
21
+ getClippingAncestors,
22
+ getEffectiveClippingAncestors,
23
+ getScrollableAncestors,
21
24
  formatTemplate,
22
25
  isValidCssMargin,
23
26
  resetValueFromWindow,
@@ -519,7 +522,7 @@ describe('calculateVisibleElementData', () => {
519
522
  height: 200,
520
523
  } as DOMRect);
521
524
 
522
- const result = calculateVisibleElementData(mockElement);
525
+ const result = calculateVisibleElementData(mockElement, true);
523
526
 
524
527
  expect(result).toEqual({
525
528
  top: 0, // Not clipped from top
@@ -529,6 +532,15 @@ describe('calculateVisibleElementData', () => {
529
532
  });
530
533
  });
531
534
 
535
+ it('should return zero dimensions when element is missing', () => {
536
+ expect(calculateVisibleElementData(null as unknown as HTMLElement)).toEqual({
537
+ top: 0,
538
+ height: 0,
539
+ left: 0,
540
+ width: 0,
541
+ });
542
+ });
543
+
532
544
  it('should calculate data for element clipped from top', () => {
533
545
  // Mock getBoundingClientRect for element partially above viewport
534
546
  jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
@@ -540,7 +552,7 @@ describe('calculateVisibleElementData', () => {
540
552
  height: 200,
541
553
  } as DOMRect);
542
554
 
543
- const result = calculateVisibleElementData(mockElement);
555
+ const result = calculateVisibleElementData(mockElement, true);
544
556
 
545
557
  expect(result).toEqual({
546
558
  top: 50, // Clipped 50px from top
@@ -561,7 +573,7 @@ describe('calculateVisibleElementData', () => {
561
573
  height: 200,
562
574
  } as DOMRect);
563
575
 
564
- const result = calculateVisibleElementData(mockElement);
576
+ const result = calculateVisibleElementData(mockElement, true);
565
577
 
566
578
  expect(result).toEqual({
567
579
  top: 0, // Not clipped from top
@@ -582,7 +594,7 @@ describe('calculateVisibleElementData', () => {
582
594
  height: 350,
583
595
  } as DOMRect);
584
596
 
585
- const result = calculateVisibleElementData(mockElement);
597
+ const result = calculateVisibleElementData(mockElement, true);
586
598
 
587
599
  expect(result).toEqual({
588
600
  top: 0, // Not clipped from top
@@ -717,6 +729,190 @@ describe('calculateVisibleElementData', () => {
717
729
  width: 1200, // Full viewport width
718
730
  });
719
731
  });
732
+
733
+ it('should calculate data clipped by a scrollable parent', () => {
734
+ const scrollContainer = document.createElement('div');
735
+ scrollContainer.style.overflow = 'auto';
736
+ scrollContainer.appendChild(mockElement);
737
+
738
+ jest.spyOn(scrollContainer, 'getBoundingClientRect').mockReturnValue({
739
+ top: 100,
740
+ left: 50,
741
+ bottom: 600,
742
+ right: 650,
743
+ width: 600,
744
+ height: 500,
745
+ } as DOMRect);
746
+
747
+ jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
748
+ top: -100,
749
+ left: 50,
750
+ bottom: 900,
751
+ right: 650,
752
+ width: 600,
753
+ height: 1000,
754
+ } as DOMRect);
755
+
756
+ const result = calculateVisibleElementData(mockElement, true);
757
+
758
+ expect(result).toEqual({
759
+ top: 200,
760
+ height: 500,
761
+ left: 0,
762
+ width: 600,
763
+ });
764
+ });
765
+
766
+ it('should calculate data clipped by a non-scroll clipping parent', () => {
767
+ const clippingContainer = document.createElement('div');
768
+ clippingContainer.style.overflow = 'hidden';
769
+ clippingContainer.appendChild(mockElement);
770
+
771
+ jest.spyOn(clippingContainer, 'getBoundingClientRect').mockReturnValue({
772
+ top: 100,
773
+ left: 100,
774
+ bottom: 500,
775
+ right: 500,
776
+ width: 400,
777
+ height: 400,
778
+ } as DOMRect);
779
+
780
+ jest.spyOn(mockElement, 'getBoundingClientRect').mockReturnValue({
781
+ top: 50,
782
+ left: 50,
783
+ bottom: 700,
784
+ right: 700,
785
+ width: 650,
786
+ height: 650,
787
+ } as DOMRect);
788
+
789
+ const result = calculateVisibleElementData(mockElement, true);
790
+
791
+ expect(result).toEqual({
792
+ top: 50,
793
+ height: 400,
794
+ left: 50,
795
+ width: 400,
796
+ });
797
+ });
798
+ });
799
+
800
+ describe('getScrollableAncestors', () => {
801
+ it('should return an empty list when element is missing', () => {
802
+ expect(getScrollableAncestors(null as unknown as HTMLElement)).toEqual([]);
803
+ });
804
+
805
+ it('should find scrollable ancestors inside a shadow root', () => {
806
+ const host = document.createElement('div');
807
+ document.body.appendChild(host);
808
+
809
+ const shadow = host.attachShadow({ mode: 'open' });
810
+ const scrollContainer = document.createElement('div');
811
+ scrollContainer.style.overflow = 'auto';
812
+ const embedTarget = document.createElement('div');
813
+ const iframe = document.createElement('iframe');
814
+
815
+ shadow.appendChild(scrollContainer);
816
+ scrollContainer.appendChild(embedTarget);
817
+ embedTarget.appendChild(iframe);
818
+
819
+ expect(getScrollableAncestors(iframe)).toEqual([scrollContainer]);
820
+
821
+ host.remove();
822
+ });
823
+
824
+ it('should ignore ancestors when computed style is unavailable', () => {
825
+ const parent = document.createElement('div');
826
+ const iframe = document.createElement('iframe');
827
+ parent.appendChild(iframe);
828
+
829
+ const getComputedStyleSpy = jest
830
+ .spyOn(window, 'getComputedStyle')
831
+ .mockReturnValue(null as unknown as CSSStyleDeclaration);
832
+
833
+ expect(getScrollableAncestors(iframe)).toEqual([]);
834
+
835
+ getComputedStyleSpy.mockRestore();
836
+ });
837
+ });
838
+
839
+ describe('getClippingAncestors', () => {
840
+ it('should return an empty list when element is missing', () => {
841
+ expect(getClippingAncestors(null as unknown as HTMLElement)).toEqual([]);
842
+ });
843
+
844
+ it('should include scrollable and non-scroll clipping ancestors', () => {
845
+ const scrollContainer = document.createElement('div');
846
+ scrollContainer.style.overflow = 'auto';
847
+ const clippingContainer = document.createElement('div');
848
+ clippingContainer.style.overflow = 'hidden';
849
+ const iframe = document.createElement('iframe');
850
+
851
+ scrollContainer.appendChild(clippingContainer);
852
+ clippingContainer.appendChild(iframe);
853
+
854
+ expect(getClippingAncestors(iframe)).toEqual([clippingContainer, scrollContainer]);
855
+ });
856
+ });
857
+
858
+ describe('getEffectiveClippingAncestors', () => {
859
+ it('should return an empty list when element is missing', () => {
860
+ expect(getEffectiveClippingAncestors(null as unknown as HTMLElement)).toEqual([]);
861
+ });
862
+
863
+ it('should ignore overflow ancestors that do not clip the element', () => {
864
+ const clippingContainer = document.createElement('div');
865
+ clippingContainer.style.overflow = 'hidden';
866
+ const iframe = document.createElement('iframe');
867
+
868
+ clippingContainer.appendChild(iframe);
869
+
870
+ jest.spyOn(clippingContainer, 'getBoundingClientRect').mockReturnValue({
871
+ top: 100,
872
+ left: 100,
873
+ bottom: 700,
874
+ right: 700,
875
+ width: 600,
876
+ height: 600,
877
+ } as DOMRect);
878
+ jest.spyOn(iframe, 'getBoundingClientRect').mockReturnValue({
879
+ top: 200,
880
+ left: 200,
881
+ bottom: 400,
882
+ right: 400,
883
+ width: 200,
884
+ height: 200,
885
+ } as DOMRect);
886
+
887
+ expect(getEffectiveClippingAncestors(iframe)).toEqual([]);
888
+ });
889
+
890
+ it('should include overflow ancestors that clip the element', () => {
891
+ const clippingContainer = document.createElement('div');
892
+ clippingContainer.style.overflow = 'hidden';
893
+ const iframe = document.createElement('iframe');
894
+
895
+ clippingContainer.appendChild(iframe);
896
+
897
+ jest.spyOn(clippingContainer, 'getBoundingClientRect').mockReturnValue({
898
+ top: 100,
899
+ left: 100,
900
+ bottom: 500,
901
+ right: 500,
902
+ width: 400,
903
+ height: 400,
904
+ } as DOMRect);
905
+ jest.spyOn(iframe, 'getBoundingClientRect').mockReturnValue({
906
+ top: 50,
907
+ left: 50,
908
+ bottom: 700,
909
+ right: 700,
910
+ width: 650,
911
+ height: 650,
912
+ } as DOMRect);
913
+
914
+ expect(getEffectiveClippingAncestors(iframe)).toEqual([clippingContainer]);
915
+ });
720
916
  });
721
917
 
722
918
  describe('formatTemplate', () => {
package/src/utils.ts CHANGED
@@ -518,22 +518,141 @@ export const handleExitPresentMode = async (): Promise<void> => {
518
518
  logger.warn('Exit fullscreen API is not supported by this browser.');
519
519
  };
520
520
 
521
- export const calculateVisibleElementData = (element: HTMLElement) => {
521
+ const scrollableOverflowPattern = /(auto|scroll|overlay)/;
522
+ const clippingOverflowPattern = /(auto|scroll|overlay|hidden|clip)/;
523
+
524
+ const getParentElementAcrossShadowRoot = (element: HTMLElement): HTMLElement | null => {
525
+ if (element.parentElement) {
526
+ return element.parentElement;
527
+ }
528
+
529
+ const rootNode = element.getRootNode?.();
530
+ if (typeof ShadowRoot !== 'undefined' && rootNode instanceof ShadowRoot) {
531
+ return rootNode.host as HTMLElement;
532
+ }
533
+
534
+ return null;
535
+ };
536
+
537
+ const hasMatchingOverflow = (element: HTMLElement, overflowPattern: RegExp) => {
538
+ const style = window.getComputedStyle(element);
539
+ return style ? overflowPattern.test(style.overflow + style.overflowX + style.overflowY) : false;
540
+ };
541
+
542
+ export const getScrollableAncestors = (element: HTMLElement) => {
543
+ const ancestors: HTMLElement[] = [];
544
+ if (!element) {
545
+ return ancestors;
546
+ }
547
+ let parent = getParentElementAcrossShadowRoot(element);
548
+
549
+ while (parent && parent !== document.body && parent !== document.documentElement) {
550
+ if (hasMatchingOverflow(parent, scrollableOverflowPattern)) {
551
+ ancestors.push(parent);
552
+ }
553
+ parent = getParentElementAcrossShadowRoot(parent);
554
+ }
555
+
556
+ return ancestors;
557
+ };
558
+
559
+ export const getClippingAncestors = (element: HTMLElement) => {
560
+ const ancestors: HTMLElement[] = [];
561
+ if (!element) {
562
+ return ancestors;
563
+ }
564
+ let parent = getParentElementAcrossShadowRoot(element);
565
+
566
+ while (parent && parent !== document.body && parent !== document.documentElement) {
567
+ if (hasMatchingOverflow(parent, clippingOverflowPattern)) {
568
+ ancestors.push(parent);
569
+ }
570
+ parent = getParentElementAcrossShadowRoot(parent);
571
+ }
572
+
573
+ return ancestors;
574
+ };
575
+
576
+ const getIntersectedRect = (
577
+ rect: Pick<DOMRect, 'top' | 'left' | 'bottom' | 'right'>,
578
+ clipRect: Pick<DOMRect, 'top' | 'left' | 'bottom' | 'right'>,
579
+ ) => ({
580
+ top: Math.max(rect.top, clipRect.top),
581
+ left: Math.max(rect.left, clipRect.left),
582
+ bottom: Math.min(rect.bottom, clipRect.bottom),
583
+ right: Math.min(rect.right, clipRect.right),
584
+ });
585
+
586
+ const isSameVisibleRect = (
587
+ rectA: ReturnType<typeof getIntersectedRect>,
588
+ rectB: ReturnType<typeof getIntersectedRect>,
589
+ ) => rectA.top === rectB.top
590
+ && rectA.left === rectB.left
591
+ && rectA.bottom === rectB.bottom
592
+ && rectA.right === rectB.right;
593
+
594
+ export const getEffectiveClippingAncestors = (element: HTMLElement) => {
595
+ if (!element) {
596
+ return [];
597
+ }
598
+
599
+ const elementRect = element.getBoundingClientRect();
600
+ let clipRect = {
601
+ top: 0,
602
+ left: 0,
603
+ bottom: window.innerHeight,
604
+ right: window.innerWidth,
605
+ };
606
+
607
+ return getClippingAncestors(element).filter((ancestor) => {
608
+ const currentVisibleRect = getIntersectedRect(elementRect, clipRect);
609
+ const nextClipRect = getIntersectedRect(clipRect, ancestor.getBoundingClientRect());
610
+ const nextVisibleRect = getIntersectedRect(elementRect, nextClipRect);
611
+ const clipsElement = !isSameVisibleRect(currentVisibleRect, nextVisibleRect);
612
+ clipRect = nextClipRect;
613
+ return clipsElement;
614
+ });
615
+ };
616
+
617
+ export const calculateVisibleElementData = (
618
+ element: HTMLElement,
619
+ useClippingAncestors = false,
620
+ ) => {
621
+ if (!element) {
622
+ return {
623
+ top: 0,
624
+ height: 0,
625
+ left: 0,
626
+ width: 0,
627
+ };
628
+ }
522
629
  const rect = element.getBoundingClientRect();
523
630
 
524
- const windowHeight = window.innerHeight;
525
- const windowWidth = window.innerWidth;
631
+ let clipTop = 0;
632
+ let clipLeft = 0;
633
+ let clipBottom = window.innerHeight;
634
+ let clipRight = window.innerWidth;
635
+
636
+ if (useClippingAncestors) {
637
+ getEffectiveClippingAncestors(element).forEach((ancestor) => {
638
+ const ancestorRect = ancestor.getBoundingClientRect();
639
+ clipTop = Math.max(clipTop, ancestorRect.top);
640
+ clipLeft = Math.max(clipLeft, ancestorRect.left);
641
+ clipBottom = Math.min(clipBottom, ancestorRect.bottom);
642
+ clipRight = Math.min(clipRight, ancestorRect.right);
643
+ });
644
+ }
526
645
 
527
- const frameRelativeTop = Math.max(rect.top, 0);
528
- const frameRelativeLeft = Math.max(rect.left, 0);
646
+ const frameRelativeTop = Math.max(rect.top, clipTop);
647
+ const frameRelativeLeft = Math.max(rect.left, clipLeft);
529
648
 
530
- const frameRelativeBottom = Math.min(windowHeight, rect.bottom);
531
- const frameRelativeRight = Math.min(windowWidth, rect.right);
649
+ const frameRelativeBottom = Math.min(clipBottom, rect.bottom);
650
+ const frameRelativeRight = Math.min(clipRight, rect.right);
532
651
 
533
652
  const data = {
534
- top: Math.max(0, rect.top * -1),
653
+ top: Math.max(0, clipTop - rect.top),
535
654
  height: Math.max(0, frameRelativeBottom - frameRelativeTop),
536
- left: Math.max(0, rect.left * -1),
655
+ left: Math.max(0, clipLeft - rect.left),
537
656
  width: Math.max(0, frameRelativeRight - frameRelativeLeft),
538
657
  };
539
658