botframework-webchat-component 4.14.1 → 4.15.0

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 (371) hide show
  1. package/.eslintrc.yml +4 -106
  2. package/.prettierrc.yml +1 -1
  3. package/lib/Activity/CarouselFilmStrip.js +1 -1
  4. package/lib/Activity/CarouselFilmStripAttachment.js +1 -1
  5. package/lib/Activity/Speak.js +1 -1
  6. package/lib/Activity/StackedLayout.d.ts.map +1 -1
  7. package/lib/Activity/StackedLayout.js +2 -2
  8. package/lib/Attachment/FileAttachment.js +2 -2
  9. package/lib/Attachment/FileContent.d.ts.map +1 -1
  10. package/lib/Attachment/FileContent.js +11 -1
  11. package/lib/BasicSendBox.js +1 -1
  12. package/lib/BasicToaster.js +5 -2
  13. package/lib/BasicTranscript.d.ts +7 -0
  14. package/lib/BasicTranscript.d.ts.map +1 -0
  15. package/lib/BasicTranscript.js +368 -725
  16. package/lib/BasicTypingIndicator.d.ts +2 -1
  17. package/lib/BasicTypingIndicator.d.ts.map +1 -1
  18. package/lib/BasicTypingIndicator.js +12 -4
  19. package/lib/Composer.d.ts.map +1 -1
  20. package/lib/Composer.js +34 -29
  21. package/lib/Middleware/Activity/createCoreMiddleware.d.ts.map +1 -1
  22. package/lib/Middleware/Activity/createCoreMiddleware.js +1 -1
  23. package/lib/Middleware/ActivityStatus/SendStatus/SendStatus.js +1 -1
  24. package/lib/Middleware/ActivityStatus/createSendStatusMiddleware.js +18 -15
  25. package/lib/Middleware/Attachment/createCoreMiddleware.js +2 -5
  26. package/lib/Middleware/AttachmentForScreenReader/createCoreMiddleware.d.ts.map +1 -1
  27. package/lib/Middleware/AttachmentForScreenReader/createCoreMiddleware.js +1 -1
  28. package/lib/Middleware/Avatar/createCoreMiddleware.d.ts.map +1 -1
  29. package/lib/Middleware/Avatar/createCoreMiddleware.js +2 -3
  30. package/lib/Middleware/CardAction/createCoreMiddleware.js +10 -3
  31. package/lib/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.d.ts.map +1 -1
  32. package/lib/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.js +1 -1
  33. package/lib/Middleware/Toast/createCoreMiddleware.d.ts.map +1 -1
  34. package/lib/Middleware/Toast/createCoreMiddleware.js +1 -1
  35. package/lib/Middleware/TypingIndicator/createCoreMiddleware.d.ts.map +1 -1
  36. package/lib/Middleware/TypingIndicator/createCoreMiddleware.js +8 -6
  37. package/lib/ScreenReaderActivity.js +14 -5
  38. package/lib/ScreenReaderText.d.ts +9 -0
  39. package/lib/ScreenReaderText.d.ts.map +1 -0
  40. package/lib/ScreenReaderText.js +22 -8
  41. package/lib/SendBox/DictationInterims.js +1 -1
  42. package/lib/SendBox/IconButton.d.ts +11 -0
  43. package/lib/SendBox/IconButton.d.ts.map +1 -0
  44. package/lib/SendBox/IconButton.js +26 -6
  45. package/lib/SendBox/MicrophoneButton.js +1 -1
  46. package/lib/SendBox/SendButton.js +1 -1
  47. package/lib/SendBox/SuggestedAction.d.ts +17 -0
  48. package/lib/SendBox/SuggestedAction.d.ts.map +1 -0
  49. package/lib/SendBox/SuggestedAction.js +25 -16
  50. package/lib/SendBox/SuggestedActions.d.ts.map +1 -1
  51. package/lib/SendBox/SuggestedActions.js +25 -8
  52. package/lib/SendBox/TextBox.js +1 -1
  53. package/lib/SendBox/UploadButton.js +1 -1
  54. package/lib/Styles/StyleSet/BasicTranscript.d.ts +13 -5
  55. package/lib/Styles/StyleSet/BasicTranscript.d.ts.map +1 -1
  56. package/lib/Styles/StyleSet/BasicTranscript.js +21 -9
  57. package/lib/Styles/StyleSet/Bubble.d.ts.map +1 -1
  58. package/lib/Styles/StyleSet/Bubble.js +1 -1
  59. package/lib/Styles/StyleSet/CarouselFilmStrip.d.ts.map +1 -1
  60. package/lib/Styles/StyleSet/CarouselFilmStrip.js +1 -1
  61. package/lib/Styles/StyleSet/CarouselFilmStripAttachment.d.ts.map +1 -1
  62. package/lib/Styles/StyleSet/CarouselFilmStripAttachment.js +1 -1
  63. package/lib/Styles/StyleSet/KeyboardHelp.d.ts +123 -0
  64. package/lib/Styles/StyleSet/KeyboardHelp.d.ts.map +1 -0
  65. package/lib/Styles/StyleSet/KeyboardHelp.js +145 -0
  66. package/lib/Styles/StyleSet/ScrollToEndButton.d.ts +1 -1
  67. package/lib/Styles/StyleSet/ScrollToEndButton.js +2 -2
  68. package/lib/Styles/StyleSet/SendBoxButton.d.ts +56 -10
  69. package/lib/Styles/StyleSet/SendBoxButton.d.ts.map +1 -1
  70. package/lib/Styles/StyleSet/SendBoxButton.js +76 -12
  71. package/lib/Styles/StyleSet/SendBoxTextBox.d.ts +1 -1
  72. package/lib/Styles/StyleSet/SendBoxTextBox.d.ts.map +1 -1
  73. package/lib/Styles/StyleSet/SendBoxTextBox.js +3 -3
  74. package/lib/Styles/StyleSet/StackedLayout.d.ts.map +1 -1
  75. package/lib/Styles/StyleSet/StackedLayout.js +1 -1
  76. package/lib/Styles/StyleSet/SuggestedAction.d.ts +73 -28
  77. package/lib/Styles/StyleSet/SuggestedAction.d.ts.map +1 -1
  78. package/lib/Styles/StyleSet/SuggestedAction.js +116 -41
  79. package/lib/Styles/StyleSet/SuggestedActions.d.ts +13 -6
  80. package/lib/Styles/StyleSet/SuggestedActions.d.ts.map +1 -1
  81. package/lib/Styles/StyleSet/SuggestedActions.js +13 -6
  82. package/lib/Styles/createStyleSet.d.ts +323 -54
  83. package/lib/Styles/createStyleSet.d.ts.map +1 -1
  84. package/lib/Styles/createStyleSet.js +4 -1
  85. package/lib/Styles/mirrorStyle.js +12 -3
  86. package/lib/Toast/NotificationIcon.js +7 -3
  87. package/lib/Toast/createToastMiddleware.d.ts.map +1 -1
  88. package/lib/Toast/createToastMiddleware.js +1 -1
  89. package/lib/Transcript/ActivityRow.d.ts +9 -0
  90. package/lib/Transcript/ActivityRow.d.ts.map +1 -0
  91. package/lib/Transcript/ActivityRow.js +157 -0
  92. package/lib/Transcript/ActivityTextAlt.js +57 -0
  93. package/lib/Transcript/FocusTrap.d.ts +8 -0
  94. package/lib/Transcript/FocusTrap.d.ts.map +1 -0
  95. package/lib/Transcript/FocusTrap.js +74 -0
  96. package/lib/Transcript/KeyboardHelp.d.ts +4 -0
  97. package/lib/Transcript/KeyboardHelp.d.ts.map +1 -0
  98. package/lib/Transcript/KeyboardHelp.js +550 -0
  99. package/lib/Transcript/LiveRegionTranscript.d.ts +8 -0
  100. package/lib/Transcript/LiveRegionTranscript.d.ts.map +1 -0
  101. package/lib/Transcript/LiveRegionTranscript.js +214 -0
  102. package/lib/Transcript/types.d.ts +2 -0
  103. package/lib/Transcript/types.d.ts.map +1 -0
  104. package/lib/Transcript/types.js +2 -0
  105. package/lib/Transcript/useActivityAccessibleName.d.ts +4 -0
  106. package/lib/Transcript/useActivityAccessibleName.d.ts.map +1 -0
  107. package/lib/Transcript/useActivityAccessibleName.js +97 -0
  108. package/lib/Utils/AccessibleButton.d.ts +11 -0
  109. package/lib/Utils/AccessibleButton.d.ts.map +1 -0
  110. package/lib/Utils/AccessibleButton.js +11 -6
  111. package/lib/Utils/FocusRedirector.d.ts +9 -0
  112. package/lib/Utils/FocusRedirector.d.ts.map +1 -0
  113. package/lib/Utils/FocusRedirector.js +16 -13
  114. package/lib/Utils/InlineMarkdown.js +12 -5
  115. package/lib/Utils/TypeFocusSink/inputtableKey.d.ts +2 -0
  116. package/lib/Utils/TypeFocusSink/inputtableKey.d.ts.map +1 -0
  117. package/lib/Utils/TypeFocusSink/inputtableKey.js +5 -2
  118. package/lib/Utils/activityAltText.d.ts +8 -0
  119. package/lib/Utils/activityAltText.d.ts.map +1 -0
  120. package/lib/Utils/createCustomEvent.js +8 -2
  121. package/lib/Utils/detectBrowser.js +4 -2
  122. package/lib/Utils/downscaleImageToDataURL/downscaleImageToDataURLUsingWorker.js +1 -1
  123. package/lib/Utils/findAncestor.js +17 -0
  124. package/lib/Utils/getActivityUniqueId.js +4 -2
  125. package/lib/Utils/intersectionOf.d.ts +5 -0
  126. package/lib/Utils/intersectionOf.d.ts.map +1 -0
  127. package/lib/Utils/intersectionOf.js +17 -2
  128. package/lib/Utils/isZeroOrPositive.d.ts +5 -0
  129. package/lib/Utils/isZeroOrPositive.d.ts.map +1 -0
  130. package/lib/Utils/isZeroOrPositive.js +4 -1
  131. package/lib/Utils/mapMap.js +9 -2
  132. package/lib/Utils/readDataURIToBlob.js +1 -1
  133. package/lib/Utils/scrollIntoViewWithBlockNearest.d.ts +7 -0
  134. package/lib/Utils/scrollIntoViewWithBlockNearest.d.ts.map +1 -0
  135. package/lib/Utils/scrollIntoViewWithBlockNearest.js +48 -0
  136. package/lib/Utils/shallowEquals.js +7 -3
  137. package/lib/Utils/supportPseudoClass.d.ts +2 -0
  138. package/lib/Utils/supportPseudoClass.d.ts.map +1 -0
  139. package/lib/Utils/supportPseudoClass.js +23 -0
  140. package/lib/Utils/tabbableElements.d.ts +2 -0
  141. package/lib/Utils/tabbableElements.d.ts.map +1 -0
  142. package/lib/Utils/tabbableElements.js +2 -2
  143. package/lib/connectToWebChat.js +13 -5
  144. package/lib/hooks/index.js +24 -24
  145. package/lib/hooks/internal/BypassSpeechSynthesisPonyfill.js +5 -4
  146. package/lib/hooks/internal/useChanged.js +8 -1
  147. package/lib/hooks/internal/useDispatchScrollPosition.js +3 -7
  148. package/lib/hooks/internal/useDispatchTranscriptFocusByActivityKey.d.ts +2 -0
  149. package/lib/hooks/internal/useDispatchTranscriptFocusByActivityKey.d.ts.map +1 -0
  150. package/lib/hooks/internal/useDispatchTranscriptFocusByActivityKey.js +15 -0
  151. package/lib/hooks/internal/useFocusVisible.d.ts +3 -0
  152. package/lib/hooks/internal/useFocusVisible.d.ts.map +1 -0
  153. package/lib/hooks/internal/useFocusVisible.js +48 -0
  154. package/lib/hooks/internal/useMemoWithPrevious.d.ts +3 -0
  155. package/lib/hooks/internal/useMemoWithPrevious.d.ts.map +1 -0
  156. package/lib/hooks/internal/useMemoWithPrevious.js +22 -0
  157. package/lib/hooks/internal/useMemoize.d.ts +14 -0
  158. package/lib/hooks/internal/useMemoize.d.ts.map +1 -0
  159. package/lib/hooks/internal/useMemoize.js +11 -2
  160. package/lib/hooks/internal/useObserveFocusVisible.d.ts +3 -0
  161. package/lib/hooks/internal/useObserveFocusVisible.d.ts.map +1 -0
  162. package/lib/hooks/internal/useObserveFocusVisible.js +208 -0
  163. package/lib/hooks/internal/usePrevious.d.ts +2 -0
  164. package/lib/hooks/internal/usePrevious.d.ts.map +1 -0
  165. package/lib/hooks/internal/usePrevious.js +18 -0
  166. package/lib/hooks/internal/useStateRef.d.ts +3 -0
  167. package/lib/hooks/internal/useStateRef.d.ts.map +1 -0
  168. package/lib/hooks/internal/useStateRef.js +40 -0
  169. package/lib/hooks/internal/useValueRef.d.ts +3 -0
  170. package/lib/hooks/internal/useValueRef.d.ts.map +1 -0
  171. package/lib/hooks/internal/useValueRef.js +25 -0
  172. package/lib/hooks/useFocus.d.ts +1 -1
  173. package/lib/hooks/useFocus.d.ts.map +1 -1
  174. package/lib/hooks/useFocus.js +1 -1
  175. package/lib/hooks/useRenderMarkdownAsHTML.d.ts.map +1 -1
  176. package/lib/hooks/useRenderMarkdownAsHTML.js +1 -1
  177. package/lib/hooks/useScrollTo.d.ts.map +1 -1
  178. package/lib/hooks/useScrollTo.js +1 -1
  179. package/lib/hooks/useScrollToEnd.d.ts.map +1 -1
  180. package/lib/hooks/useScrollToEnd.js +1 -1
  181. package/lib/index.d.ts +10 -0
  182. package/lib/index.d.ts.map +1 -1
  183. package/lib/index.js +17 -15
  184. package/lib/providers/ActivityTree/ActivityTreeComposer.d.ts +5 -0
  185. package/lib/providers/ActivityTree/ActivityTreeComposer.d.ts.map +1 -0
  186. package/lib/providers/ActivityTree/ActivityTreeComposer.js +86 -0
  187. package/lib/providers/ActivityTree/private/Context.d.ts +9 -0
  188. package/lib/providers/ActivityTree/private/Context.d.ts.map +1 -0
  189. package/lib/providers/ActivityTree/private/Context.js +13 -0
  190. package/lib/providers/ActivityTree/private/types.d.ts +10 -0
  191. package/lib/providers/ActivityTree/private/types.d.ts.map +1 -0
  192. package/lib/providers/ActivityTree/private/types.js +2 -0
  193. package/lib/providers/ActivityTree/private/useActivitiesWithRenderer.d.ts +4 -0
  194. package/lib/providers/ActivityTree/private/useActivitiesWithRenderer.d.ts.map +1 -0
  195. package/lib/providers/ActivityTree/private/useActivitiesWithRenderer.js +58 -0
  196. package/lib/providers/ActivityTree/private/useActivityTreeWithRenderer.d.ts +5 -0
  197. package/lib/providers/ActivityTree/private/useActivityTreeWithRenderer.d.ts.map +1 -0
  198. package/lib/providers/ActivityTree/private/useActivityTreeWithRenderer.js +166 -0
  199. package/lib/providers/ActivityTree/private/useContext.d.ts +3 -0
  200. package/lib/providers/ActivityTree/private/useContext.d.ts.map +1 -0
  201. package/lib/providers/ActivityTree/private/useContext.js +24 -0
  202. package/lib/providers/ActivityTree/useActivityTreeWithRenderer.d.ts +8 -0
  203. package/lib/providers/ActivityTree/useActivityTreeWithRenderer.d.ts.map +1 -0
  204. package/lib/providers/ActivityTree/useActivityTreeWithRenderer.js +17 -0
  205. package/lib/providers/LiveRegionTwin/LiveRegionTwinComposer.d.ts +35 -0
  206. package/lib/providers/LiveRegionTwin/LiveRegionTwinComposer.d.ts.map +1 -0
  207. package/lib/providers/LiveRegionTwin/LiveRegionTwinComposer.js +156 -0
  208. package/lib/providers/LiveRegionTwin/private/Context.d.ts +10 -0
  209. package/lib/providers/LiveRegionTwin/private/Context.d.ts.map +1 -0
  210. package/lib/providers/LiveRegionTwin/private/Context.js +13 -0
  211. package/lib/providers/LiveRegionTwin/private/LiveRegionTwinContainer.d.ts +11 -0
  212. package/lib/providers/LiveRegionTwin/private/LiveRegionTwinContainer.d.ts.map +1 -0
  213. package/lib/providers/LiveRegionTwin/private/LiveRegionTwinContainer.js +86 -0
  214. package/lib/providers/LiveRegionTwin/private/types.d.ts +8 -0
  215. package/lib/providers/LiveRegionTwin/private/types.d.ts.map +1 -0
  216. package/lib/providers/LiveRegionTwin/private/types.js +2 -0
  217. package/lib/providers/LiveRegionTwin/private/useContext.d.ts +3 -0
  218. package/lib/providers/LiveRegionTwin/private/useContext.d.ts.map +1 -0
  219. package/lib/providers/LiveRegionTwin/private/useContext.js +24 -0
  220. package/lib/providers/LiveRegionTwin/private/useMarkAllAsRenderedEffect.d.ts +2 -0
  221. package/lib/providers/LiveRegionTwin/private/useMarkAllAsRenderedEffect.d.ts.map +1 -0
  222. package/lib/providers/LiveRegionTwin/private/useMarkAllAsRenderedEffect.js +24 -0
  223. package/lib/providers/LiveRegionTwin/private/useStaticElementEntries.d.ts +3 -0
  224. package/lib/providers/LiveRegionTwin/private/useStaticElementEntries.d.ts.map +1 -0
  225. package/lib/providers/LiveRegionTwin/private/useStaticElementEntries.js +15 -0
  226. package/lib/providers/LiveRegionTwin/useQueueStaticElement.d.ts +8 -0
  227. package/lib/providers/LiveRegionTwin/useQueueStaticElement.d.ts.map +1 -0
  228. package/lib/providers/LiveRegionTwin/useQueueStaticElement.js +20 -0
  229. package/lib/providers/TranscriptFocus/TranscriptFocusComposer.d.ts +7 -0
  230. package/lib/providers/TranscriptFocus/TranscriptFocusComposer.d.ts.map +1 -0
  231. package/lib/providers/TranscriptFocus/TranscriptFocusComposer.js +184 -0
  232. package/lib/providers/TranscriptFocus/private/Context.d.ts +12 -0
  233. package/lib/providers/TranscriptFocus/private/Context.d.ts.map +1 -0
  234. package/lib/providers/TranscriptFocus/private/Context.js +13 -0
  235. package/lib/providers/TranscriptFocus/private/useContext.d.ts +2 -0
  236. package/lib/providers/TranscriptFocus/private/useContext.d.ts.map +1 -0
  237. package/lib/providers/TranscriptFocus/private/useContext.js +24 -0
  238. package/lib/providers/TranscriptFocus/useActiveDescendantId.d.ts +2 -0
  239. package/lib/providers/TranscriptFocus/useActiveDescendantId.d.ts.map +1 -0
  240. package/lib/providers/TranscriptFocus/useActiveDescendantId.js +15 -0
  241. package/lib/providers/TranscriptFocus/useFocusByActivityKey.d.ts +8 -0
  242. package/lib/providers/TranscriptFocus/useFocusByActivityKey.d.ts.map +1 -0
  243. package/lib/providers/TranscriptFocus/useFocusByActivityKey.js +21 -0
  244. package/lib/providers/TranscriptFocus/useFocusRelativeActivity.d.ts +2 -0
  245. package/lib/providers/TranscriptFocus/useFocusRelativeActivity.d.ts.map +1 -0
  246. package/lib/providers/TranscriptFocus/useFocusRelativeActivity.js +15 -0
  247. package/lib/providers/TranscriptFocus/useFocusedActivityKey.d.ts +2 -0
  248. package/lib/providers/TranscriptFocus/useFocusedActivityKey.d.ts.map +1 -0
  249. package/lib/providers/TranscriptFocus/useFocusedActivityKey.js +15 -0
  250. package/lib/providers/TranscriptFocus/useFocusedExplicitly.d.ts +2 -0
  251. package/lib/providers/TranscriptFocus/useFocusedExplicitly.d.ts.map +1 -0
  252. package/lib/providers/TranscriptFocus/useFocusedExplicitly.js +15 -0
  253. package/lib/providers/TranscriptFocus/useGetDescendantIdByActivityKey.d.ts +2 -0
  254. package/lib/providers/TranscriptFocus/useGetDescendantIdByActivityKey.d.ts.map +1 -0
  255. package/lib/providers/TranscriptFocus/useGetDescendantIdByActivityKey.js +15 -0
  256. package/package.json +17 -24
  257. package/src/Activity/CarouselFilmStripAttachment.js +0 -1
  258. package/src/Activity/StackedLayout.tsx +10 -9
  259. package/src/Attachment/FileAttachment.js +1 -1
  260. package/src/Attachment/FileContent.tsx +12 -0
  261. package/src/BasicToaster.js +5 -1
  262. package/src/BasicTranscript.tsx +878 -0
  263. package/src/BasicTypingIndicator.tsx +3 -2
  264. package/src/Composer.tsx +41 -29
  265. package/src/Middleware/Activity/createCoreMiddleware.tsx +50 -44
  266. package/src/Middleware/ActivityStatus/createSendStatusMiddleware.js +2 -0
  267. package/src/Middleware/Attachment/createCoreMiddleware.tsx +28 -28
  268. package/src/Middleware/AttachmentForScreenReader/createCoreMiddleware.tsx +25 -23
  269. package/src/Middleware/Avatar/createCoreMiddleware.tsx +9 -8
  270. package/src/Middleware/CardAction/createCoreMiddleware.js +9 -2
  271. package/src/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.ts +11 -9
  272. package/src/Middleware/Toast/createCoreMiddleware.tsx +12 -10
  273. package/src/Middleware/TypingIndicator/createCoreMiddleware.tsx +8 -2
  274. package/src/ScreenReaderActivity.js +18 -3
  275. package/src/{ScreenReaderText.js → ScreenReaderText.tsx} +27 -9
  276. package/src/SendBox/{IconButton.js → IconButton.tsx} +19 -3
  277. package/src/SendBox/{SuggestedAction.js → SuggestedAction.tsx} +72 -35
  278. package/src/SendBox/SuggestedActions.tsx +47 -20
  279. package/src/Styles/StyleSet/BasicTranscript.ts +34 -20
  280. package/src/Styles/StyleSet/Bubble.ts +0 -1
  281. package/src/Styles/StyleSet/CarouselFilmStrip.ts +12 -10
  282. package/src/Styles/StyleSet/CarouselFilmStripAttachment.ts +4 -3
  283. package/src/Styles/StyleSet/KeyboardHelp.ts +157 -0
  284. package/src/Styles/StyleSet/ScrollToEndButton.ts +1 -1
  285. package/src/Styles/StyleSet/SendBoxButton.ts +84 -13
  286. package/src/Styles/StyleSet/SendBoxTextBox.ts +1 -2
  287. package/src/Styles/StyleSet/StackedLayout.ts +13 -11
  288. package/src/Styles/StyleSet/SuggestedAction.ts +129 -42
  289. package/src/Styles/StyleSet/SuggestedActions.ts +13 -5
  290. package/src/Styles/createStyleSet.ts +2 -1
  291. package/src/Styles/mirrorStyle.js +10 -2
  292. package/src/Toast/NotificationIcon.js +4 -1
  293. package/src/Toast/createToastMiddleware.tsx +4 -1
  294. package/src/Transcript/ActivityRow.tsx +124 -0
  295. package/src/Transcript/ActivityTextAlt.tsx +32 -0
  296. package/src/Transcript/FocusTrap.tsx +64 -0
  297. package/src/Transcript/KeyboardHelp.tsx +282 -0
  298. package/src/Transcript/LiveRegionTranscript.tsx +181 -0
  299. package/src/Transcript/types.ts +1 -0
  300. package/src/Transcript/useActivityAccessibleName.ts +85 -0
  301. package/src/Utils/{AccessibleButton.js → AccessibleButton.tsx} +19 -4
  302. package/src/Utils/{FocusRedirector.js → FocusRedirector.tsx} +21 -8
  303. package/src/Utils/InlineMarkdown.js +18 -2
  304. package/src/Utils/TypeFocusSink/inputtableKey.ts +5 -1
  305. package/src/Utils/createCustomEvent.js +7 -1
  306. package/src/Utils/detectBrowser.js +2 -1
  307. package/src/Utils/findAncestor.ts +12 -0
  308. package/src/Utils/getActivityUniqueId.ts +5 -0
  309. package/src/Utils/intersectionOf.ts +14 -0
  310. package/src/Utils/isZeroOrPositive.ts +7 -0
  311. package/src/Utils/mapMap.js +7 -1
  312. package/src/Utils/scrollIntoViewWithBlockNearest.ts +20 -0
  313. package/src/Utils/shallowEquals.js +8 -1
  314. package/src/Utils/supportPseudoClass.ts +17 -0
  315. package/src/Utils/{tabbableElements.js → tabbableElements.ts} +6 -5
  316. package/src/connectToWebChat.js +11 -4
  317. package/src/hooks/internal/BypassSpeechSynthesisPonyfill.js +3 -1
  318. package/src/hooks/internal/useChanged.ts +17 -0
  319. package/src/hooks/internal/useDispatchScrollPosition.js +1 -3
  320. package/src/hooks/internal/useDispatchTranscriptFocusByActivityKey.ts +5 -0
  321. package/src/hooks/internal/useFocusVisible.ts +22 -0
  322. package/src/hooks/internal/useMemoWithPrevious.ts +16 -0
  323. package/src/hooks/internal/useMemoize.spec.js +1 -1
  324. package/src/hooks/internal/useMemoize.ts +53 -0
  325. package/src/hooks/internal/useObserveFocusVisible.ts +252 -0
  326. package/src/hooks/internal/usePrevious.ts +12 -0
  327. package/src/hooks/internal/useStateRef.ts +31 -0
  328. package/src/hooks/internal/useValueRef.ts +22 -0
  329. package/src/hooks/useFocus.ts +1 -1
  330. package/src/hooks/useRenderMarkdownAsHTML.ts +4 -5
  331. package/src/hooks/useScrollTo.ts +4 -3
  332. package/src/hooks/useScrollToEnd.ts +4 -3
  333. package/src/index.ts +0 -1
  334. package/src/providers/ActivityTree/ActivityTreeComposer.tsx +74 -0
  335. package/src/providers/ActivityTree/private/Context.ts +12 -0
  336. package/src/providers/ActivityTree/private/types.ts +12 -0
  337. package/src/providers/ActivityTree/private/useActivitiesWithRenderer.ts +66 -0
  338. package/src/providers/ActivityTree/private/useActivityTreeWithRenderer.ts +140 -0
  339. package/src/providers/ActivityTree/private/useContext.ts +15 -0
  340. package/src/providers/ActivityTree/useActivityTreeWithRenderer.ts +16 -0
  341. package/src/providers/LiveRegionTwin/LiveRegionTwinComposer.tsx +154 -0
  342. package/src/providers/LiveRegionTwin/private/Context.ts +15 -0
  343. package/src/providers/LiveRegionTwin/private/LiveRegionTwinContainer.tsx +64 -0
  344. package/src/providers/LiveRegionTwin/private/types.ts +10 -0
  345. package/src/providers/LiveRegionTwin/private/useContext.ts +15 -0
  346. package/src/providers/LiveRegionTwin/private/useMarkAllAsRenderedEffect.ts +13 -0
  347. package/src/providers/LiveRegionTwin/private/useStaticElementEntries.ts +7 -0
  348. package/src/providers/LiveRegionTwin/useQueueStaticElement.ts +12 -0
  349. package/src/providers/TranscriptFocus/TranscriptFocusComposer.tsx +180 -0
  350. package/src/providers/TranscriptFocus/private/Context.ts +16 -0
  351. package/src/providers/TranscriptFocus/private/useContext.ts +13 -0
  352. package/src/providers/TranscriptFocus/useActiveDescendantId.ts +5 -0
  353. package/src/providers/TranscriptFocus/useFocusByActivityKey.ts +14 -0
  354. package/src/providers/TranscriptFocus/useFocusRelativeActivity.ts +5 -0
  355. package/src/providers/TranscriptFocus/useFocusedActivityKey.ts +5 -0
  356. package/src/providers/TranscriptFocus/useFocusedExplicitly.ts +5 -0
  357. package/src/providers/TranscriptFocus/useGetDescendantIdByActivityKey.ts +5 -0
  358. package/.eslintignore +0 -1
  359. package/lib/Utils/findLastIndex.js +0 -32
  360. package/lib/hooks/internal/useAcknowledgedActivity.js +0 -90
  361. package/lib/hooks/internal/useDispatchTranscriptFocus.js +0 -19
  362. package/src/BasicTranscript.js +0 -1139
  363. package/src/Utils/findLastIndex.js +0 -11
  364. package/src/Utils/findLastIndex.spec.js +0 -31
  365. package/src/Utils/getActivityUniqueId.js +0 -3
  366. package/src/Utils/intersectionOf.js +0 -11
  367. package/src/Utils/isZeroOrPositive.js +0 -4
  368. package/src/hooks/internal/useAcknowledgedActivity.js +0 -65
  369. package/src/hooks/internal/useChanged.js +0 -10
  370. package/src/hooks/internal/useDispatchTranscriptFocus.js +0 -7
  371. package/src/hooks/internal/useMemoize.js +0 -37
@@ -1,1139 +0,0 @@
1
- /* eslint no-magic-numbers: ["error", { "ignore": [-1, 0, 1, 2, 5, 36] }] */
2
-
3
- import { hooks } from 'botframework-webchat-api';
4
- import {
5
- Composer as ReactScrollToBottomComposer,
6
- Panel as ReactScrollToBottomPanel,
7
- useAnimatingToEnd,
8
- useAtEnd,
9
- useObserveScrollPosition,
10
- useScrollTo,
11
- useScrollToEnd,
12
- useSticky
13
- } from 'react-scroll-to-bottom';
14
- import classNames from 'classnames';
15
- import PropTypes from 'prop-types';
16
- import random from 'math-random';
17
- import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
18
-
19
- import BasicTypingIndicator from './BasicTypingIndicator';
20
- import Fade from './Utils/Fade';
21
- import FocusRedirector from './Utils/FocusRedirector';
22
- import getActivityUniqueId from './Utils/getActivityUniqueId';
23
- import getTabIndex from './Utils/TypeFocusSink/getTabIndex';
24
- import inputtableKey from './Utils/TypeFocusSink/inputtableKey';
25
- import intersectionOf from './Utils/intersectionOf';
26
- import isZeroOrPositive from './Utils/isZeroOrPositive';
27
- import removeInline from './Utils/removeInline';
28
- import ScreenReaderActivity from './ScreenReaderActivity';
29
- import ScreenReaderText from './ScreenReaderText';
30
- import SpeakActivity from './Activity/Speak';
31
- import tabbableElements from './Utils/tabbableElements';
32
- import useAcknowledgedActivity from './hooks/internal/useAcknowledgedActivity';
33
- import useDispatchScrollPosition from './hooks/internal/useDispatchScrollPosition';
34
- import useDispatchTranscriptFocus from './hooks/internal/useDispatchTranscriptFocus';
35
- import useFocus from './hooks/useFocus';
36
- import useMemoize from './hooks/internal/useMemoize';
37
- import useRegisterFocusTranscript from './hooks/internal/useRegisterFocusTranscript';
38
- import useRegisterScrollRelative from './hooks/internal/useRegisterScrollRelative';
39
- import useRegisterScrollTo from './hooks/internal/useRegisterScrollTo';
40
- import useRegisterScrollToEnd from './hooks/internal/useRegisterScrollToEnd';
41
- import useStyleSet from './hooks/useStyleSet';
42
- import useStyleToEmotionObject from './hooks/internal/useStyleToEmotionObject';
43
- import useUniqueId from './hooks/internal/useUniqueId';
44
-
45
- const {
46
- useActivities,
47
- useCreateActivityRenderer,
48
- useCreateActivityStatusRenderer,
49
- useCreateAvatarRenderer,
50
- useCreateScrollToEndButtonRenderer,
51
- useDirection,
52
- useGroupActivities,
53
- useLocalizer,
54
- useStyleOptions
55
- } = hooks;
56
-
57
- const ROOT_STYLE = {
58
- '&.webchat__basic-transcript': {
59
- display: 'flex',
60
- flexDirection: 'column',
61
- overflow: 'hidden',
62
- // Make sure to set "position: relative" here to form another stacking context for the scroll-to-end button.
63
- // Stacking context help isolating elements that use "z-index" from global pollution.
64
- // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
65
- position: 'relative',
66
-
67
- '& .webchat__basic-transcript__filler': {
68
- flex: 1
69
- },
70
-
71
- '& .webchat__basic-transcript__scrollable': {
72
- display: 'flex',
73
- flexDirection: 'column',
74
- overflowX: 'hidden',
75
- WebkitOverflowScrolling: 'touch'
76
- },
77
-
78
- '& .webchat__basic-transcript__transcript': {
79
- listStyleType: 'none'
80
- }
81
- }
82
- };
83
-
84
- function validateAllActivitiesTagged(activities, bins) {
85
- return activities.every(activity => bins.some(bin => bin.includes(activity)));
86
- }
87
-
88
- const InternalTranscript = ({ activityElementsRef, className }) => {
89
- const [{ basicTranscript: basicTranscriptStyleSet }] = useStyleSet();
90
- const [{ bubbleFromUserNubOffset, bubbleNubOffset, groupTimestamp, internalLiveRegionFadeAfter, showAvatarInGroup }] =
91
- useStyleOptions();
92
- const [focusedActivityKey, setFocusedActivityKey] = useState();
93
- const [activities] = useActivities();
94
- const [direction] = useDirection();
95
- const createActivityRenderer = useCreateActivityRenderer();
96
- const createActivityStatusRenderer = useCreateActivityStatusRenderer();
97
- const createAvatarRenderer = useCreateAvatarRenderer();
98
- const focus = useFocus();
99
- const groupActivities = useGroupActivities();
100
- const localize = useLocalizer();
101
- const rootClassName = useStyleToEmotionObject()(ROOT_STYLE) + '';
102
- const rootElementRef = useRef();
103
- const terminatorRef = useRef();
104
-
105
- const activityInteractiveAlt = localize('ACTIVITY_INTERACTIVE_LABEL_ALT');
106
- const terminatorText = localize('TRANSCRIPT_TERMINATOR_TEXT');
107
- const transcriptAriaLabel = localize('TRANSCRIPT_ARIA_LABEL_ALT');
108
- const transcriptRoleDescription = localize('TRANSCRIPT_ARIA_ROLE_ALT');
109
-
110
- const hideAllTimestamps = groupTimestamp === false;
111
-
112
- // Gets renderer for every activity.
113
- // Activities that are not visible will return a falsy renderer.
114
-
115
- // Converted from createActivityRenderer({ activity, nextVisibleActivity }) to createActivityRenderer(activity, nextVisibleActivity).
116
- // This is for the memoization function to cache the arguments. Memoizer can only cache literal arguments.
117
- const createActivityRendererWithLiteralArgs = useCallback(
118
- (activity, nextVisibleActivity) => createActivityRenderer({ activity, nextVisibleActivity }),
119
- [createActivityRenderer]
120
- );
121
-
122
- // Create a memoized context of the createActivityRenderer function.
123
- const activitiesWithRenderer = useMemoize(
124
- createActivityRendererWithLiteralArgs,
125
- createActivityRendererWithLiteralArgsMemoized => {
126
- // All calls to createActivityRendererWithLiteralArgsMemoized() in this function will be memoized (LRU = 1).
127
- // In the next render cycle, calls to createActivityRendererWithLiteralArgsMemoized() might return the memoized result instead.
128
- // This is an improvement to React useMemo(), because it only allows 1 memoization.
129
- // useMemoize() allows any number of memoization.
130
-
131
- const activitiesWithRenderer = [];
132
- let nextVisibleActivity;
133
-
134
- for (let index = activities.length - 1; index >= 0; index--) {
135
- const activity = activities[index];
136
- const renderActivity = createActivityRendererWithLiteralArgsMemoized(activity, nextVisibleActivity);
137
-
138
- if (renderActivity) {
139
- activitiesWithRenderer.splice(0, 0, {
140
- activity,
141
- renderActivity
142
- });
143
-
144
- nextVisibleActivity = activity;
145
- }
146
- }
147
-
148
- return activitiesWithRenderer;
149
- },
150
- [activities]
151
- );
152
-
153
- const visibleActivities = useMemo(
154
- () => activitiesWithRenderer.map(({ activity }) => activity),
155
- [activitiesWithRenderer]
156
- );
157
-
158
- // Tag activities based on types.
159
- // The default implementation tag into 2 types: sender and status.
160
-
161
- const { activitiesGroupBySender, activitiesGroupByStatus } = useMemo(() => {
162
- const { sender: activitiesGroupBySender, status: activitiesGroupByStatus } = groupActivities({
163
- activities: visibleActivities
164
- });
165
-
166
- if (!validateAllActivitiesTagged(visibleActivities, activitiesGroupBySender)) {
167
- console.warn(
168
- 'botframework-webchat: Not every activities are grouped in the "sender" property. Please fix "groupActivitiesMiddleware" and group every activities.'
169
- );
170
- }
171
-
172
- if (!validateAllActivitiesTagged(visibleActivities, activitiesGroupByStatus)) {
173
- console.warn(
174
- 'botframework-webchat: Not every activities are grouped in the "status" property. Please fix "groupActivitiesMiddleware" and group every activities.'
175
- );
176
- }
177
-
178
- return {
179
- activitiesGroupBySender,
180
- activitiesGroupByStatus
181
- };
182
- }, [groupActivities, visibleActivities]);
183
-
184
- // Create a tree of activities with 2 dimensions: sender, followed by status.
185
-
186
- const activityTree = useMemo(() => {
187
- const visibleActivitiesPendingGrouping = [...visibleActivities];
188
- const activityTree = [];
189
-
190
- while (visibleActivitiesPendingGrouping.length) {
191
- const [activity] = visibleActivitiesPendingGrouping;
192
- const senderTree = [];
193
- const activitiesWithSameSender = activitiesGroupBySender.find(activities => activities.includes(activity));
194
-
195
- activityTree.push(senderTree);
196
-
197
- activitiesWithSameSender.forEach(activity => {
198
- const activitiesWithSameStatus = activitiesGroupByStatus.find(activities => activities.includes(activity));
199
-
200
- const activitiesWithSameSenderAndStatus = intersectionOf(
201
- visibleActivitiesPendingGrouping,
202
- activitiesWithSameSender,
203
- activitiesWithSameStatus
204
- );
205
-
206
- if (activitiesWithSameSenderAndStatus.length) {
207
- senderTree.push(activitiesWithSameSenderAndStatus);
208
- removeInline(visibleActivitiesPendingGrouping, ...activitiesWithSameSenderAndStatus);
209
- }
210
- });
211
- }
212
-
213
- // Assertion: All activities in visibleActivities, must be assigned to the activityTree
214
- if (
215
- !visibleActivities.every(activity =>
216
- activityTree.some(activitiesWithSameSender =>
217
- activitiesWithSameSender.some(activitiesWithSameSenderAndStatus =>
218
- activitiesWithSameSenderAndStatus.includes(activity)
219
- )
220
- )
221
- )
222
- ) {
223
- console.warn('botframework-webchat internal: Not all visible activities are grouped in the activityTree.', {
224
- visibleActivities,
225
- activityTree
226
- });
227
- }
228
-
229
- return activityTree;
230
- }, [activitiesGroupBySender, activitiesGroupByStatus, visibleActivities]);
231
-
232
- // Flatten the tree back into an array with information related to rendering.
233
-
234
- const renderingElements = useMemo(() => {
235
- const renderingElements = [];
236
- const topSideBotNub = isZeroOrPositive(bubbleNubOffset);
237
- const topSideUserNub = isZeroOrPositive(bubbleFromUserNubOffset);
238
-
239
- activityTree.forEach(activitiesWithSameSender => {
240
- const [[firstActivity]] = activitiesWithSameSender;
241
- const renderAvatar = createAvatarRenderer({ activity: firstActivity });
242
-
243
- activitiesWithSameSender.forEach((activitiesWithSameSenderAndStatus, indexWithinSenderGroup) => {
244
- const firstInSenderGroup = !indexWithinSenderGroup;
245
- const lastInSenderGroup = indexWithinSenderGroup === activitiesWithSameSender.length - 1;
246
-
247
- activitiesWithSameSenderAndStatus.forEach((activity, indexWithinSenderAndStatusGroup) => {
248
- // We only show the timestamp at the end of the sender group. But we always show the "Send failed, retry" prompt.
249
- const renderActivityStatus = createActivityStatusRenderer({
250
- activity
251
- });
252
-
253
- const firstInSenderAndStatusGroup = !indexWithinSenderAndStatusGroup;
254
- const lastInSenderAndStatusGroup =
255
- indexWithinSenderAndStatusGroup === activitiesWithSameSenderAndStatus.length - 1;
256
-
257
- const { renderActivity } = activitiesWithRenderer.find(entry => entry.activity === activity);
258
- const key = getActivityUniqueId(activity) || renderingElements.length;
259
- const baseAltText =
260
- typeof activity?.channelData?.['webchat:fallback-text'] === 'string'
261
- ? activity?.channelData?.['webchat:fallback-text']
262
- : activity?.channelData?.messageBack?.displayText || activity.text;
263
-
264
- // If "webchat:fallback-text" field is set to empty string, the activity must not be narrated.
265
- const supportScreenReader = activity?.channelData?.['webchat:fallback-text'] !== '';
266
-
267
- const {
268
- from: { role }
269
- } = activity;
270
-
271
- const topSideNub = role === 'user' ? topSideUserNub : topSideBotNub;
272
-
273
- let showCallout;
274
-
275
- // Depending on the "showAvatarInGroup" setting, the avatar will render in different positions.
276
- if (showAvatarInGroup === 'sender') {
277
- if (topSideNub) {
278
- showCallout = firstInSenderGroup && firstInSenderAndStatusGroup;
279
- } else {
280
- showCallout = lastInSenderGroup && lastInSenderAndStatusGroup;
281
- }
282
- } else if (showAvatarInGroup === 'status') {
283
- if (topSideNub) {
284
- showCallout = firstInSenderAndStatusGroup;
285
- } else {
286
- showCallout = lastInSenderAndStatusGroup;
287
- }
288
- } else {
289
- showCallout = true;
290
- }
291
-
292
- const focusActivity = () => {
293
- setFocusedActivityKey(getActivityUniqueId(activity));
294
-
295
- // IE11 need to manually focus on the transcript.
296
- const { current: rootElement } = rootElementRef;
297
-
298
- rootElement && rootElement.focus();
299
- };
300
-
301
- renderingElements.push({
302
- activity,
303
-
304
- // After the element is mounted, set it to activityElementsRef.
305
- callbackRef: activityElement => {
306
- const entry = activityElementsRef.current.find(({ activityID }) => activityID === activity.id);
307
-
308
- if (entry) {
309
- entry.element = activityElement;
310
- }
311
- },
312
-
313
- // Calling this function will put the focus on the transcript and the activity.
314
- focusActivity,
315
-
316
- // When a child of the activity receives focus, notify the transcript to set the aria-activedescendant to this activity.
317
- handleFocus: () => {
318
- setFocusedActivityKey(getActivityUniqueId(activity));
319
- },
320
-
321
- handleKeyDown: event => {
322
- if (event.key === 'Escape') {
323
- event.preventDefault();
324
- event.stopPropagation();
325
-
326
- setFocusedActivityKey(getActivityUniqueId(activity));
327
-
328
- const { current } = rootElementRef;
329
-
330
- current && current.focus();
331
- }
332
- },
333
-
334
- // For accessibility: when the user press up/down arrow keys, we put a visual focus indicator around the focused activity.
335
- // We should do the same for mouse, that is why we have the click handler here.
336
- // We are doing it in event capture phase to prevent other components from stopping event propagation to us.
337
- handleMouseDownCapture: ({ target }) => {
338
- const tabIndex = getTabIndex(target);
339
-
340
- if (typeof tabIndex !== 'number' || tabIndex < 0 || target.getAttribute('aria-disabled') === 'true') {
341
- focusActivity();
342
- }
343
- },
344
-
345
- // "hideTimestamp" is a render-time parameter for renderActivityStatus().
346
- // If true, it will hide the timestamp, but it will continue to show the
347
- // retry prompt. And show the screen reader version of the timestamp.
348
- hideTimestamp:
349
- hideAllTimestamps || indexWithinSenderAndStatusGroup !== activitiesWithSameSenderAndStatus.length - 1,
350
- key,
351
-
352
- // When "liveRegionKey" changes or contents that made up the alt text changed, it will show up in the live region momentarily.
353
- liveRegionKey: key + '|' + baseAltText,
354
- renderActivity,
355
- renderActivityStatus,
356
- renderAvatar,
357
- role,
358
-
359
- // TODO: [P2] #2858 We should use core/definitions/speakingActivity for this predicate instead
360
- shouldSpeak: activity.channelData && activity.channelData.speak,
361
- showCallout,
362
- supportScreenReader
363
- });
364
- });
365
- });
366
- });
367
-
368
- const { current: activityElements } = activityElementsRef;
369
-
370
- // Update activityElementRef with new sets of activity, while retaining the existing referencing element if exists.
371
- activityElementsRef.current = renderingElements.map(({ activity, activity: { id }, elementId, key }) => {
372
- const existingEntry = activityElements.find(entry => entry.key === key);
373
-
374
- return {
375
- activity,
376
- activityID: id,
377
- ariaLabelID: existingEntry
378
- ? existingEntry.ariaLabelID
379
- : `webchat__basic-transcript__activity-label-${random().toString(36).substr(2, 5)}`,
380
- element: existingEntry && existingEntry.element,
381
- elementId,
382
- key
383
- };
384
- });
385
-
386
- // There must be one focused (a.k.a. aria-activedescendant) designated. We default it to the last one.
387
- if (!renderingElements.find(({ focused }) => focused)) {
388
- const lastElement = renderingElements[renderingElements.length - 1];
389
-
390
- if (lastElement) {
391
- lastElement.focused = true;
392
- }
393
- }
394
-
395
- return renderingElements;
396
- }, [
397
- activitiesWithRenderer,
398
- activityElementsRef,
399
- activityTree,
400
- bubbleFromUserNubOffset,
401
- bubbleNubOffset,
402
- createActivityStatusRenderer,
403
- createAvatarRenderer,
404
- hideAllTimestamps,
405
- rootElementRef,
406
- showAvatarInGroup
407
- ]);
408
-
409
- const renderingActivities = useMemo(() => renderingElements.map(({ activity }) => activity), [renderingElements]);
410
-
411
- const scrollToBottomScrollTo = useScrollTo();
412
- const scrollToBottomScrollToEnd = useScrollToEnd();
413
-
414
- const scrollTo = useCallback(
415
- (position, { behavior = 'auto' } = {}) => {
416
- if (!position) {
417
- throw new Error(
418
- 'botframework-webchat: First argument passed to "useScrollTo" must be a ScrollPosition object.'
419
- );
420
- }
421
-
422
- const { activityID, scrollTop } = position;
423
-
424
- if (typeof scrollTop !== 'undefined') {
425
- scrollToBottomScrollTo(scrollTop, { behavior });
426
- } else if (typeof activityID !== 'undefined') {
427
- const { current: rootElement } = rootElementRef;
428
- const { element: activityElement } =
429
- activityElementsRef.current.find(entry => entry.activityID === activityID) || {};
430
-
431
- const scrollableElement = rootElement.querySelector('.webchat__basic-transcript__scrollable');
432
-
433
- if (scrollableElement && activityElement) {
434
- const [{ height: activityElementHeight, y: activityElementY }] = activityElement.getClientRects();
435
- const [{ height: scrollableHeight }] = scrollableElement.getClientRects();
436
-
437
- const activityElementOffsetTop = activityElementY + scrollableElement.scrollTop;
438
-
439
- const scrollTop = Math.min(
440
- activityElementOffsetTop,
441
- activityElementOffsetTop - scrollableHeight + activityElementHeight
442
- );
443
-
444
- scrollToBottomScrollTo(scrollTop, { behavior });
445
- }
446
- }
447
- },
448
- [activityElementsRef, rootElementRef, scrollToBottomScrollTo]
449
- );
450
-
451
- const scrollToEnd = useCallback(() => scrollToBottomScrollToEnd({ behavior: 'smooth' }), [scrollToBottomScrollToEnd]);
452
-
453
- const scrollRelative = useCallback(
454
- (direction, { displacement } = {}) => {
455
- const { current: rootElement } = rootElementRef;
456
-
457
- if (!rootElement) {
458
- return;
459
- }
460
-
461
- const scrollable = rootElement.querySelector('.webchat__basic-transcript__scrollable');
462
- let nextScrollTop;
463
-
464
- if (typeof displacement === 'number') {
465
- nextScrollTop = scrollable.scrollTop + (direction === 'down' ? 1 : -1) * displacement;
466
- } else {
467
- nextScrollTop = scrollable.scrollTop + (direction === 'down' ? 1 : -1) * scrollable.offsetHeight;
468
- }
469
-
470
- scrollTo(
471
- {
472
- scrollTop: Math.max(0, Math.min(scrollable.scrollHeight - scrollable.offsetHeight, nextScrollTop))
473
- },
474
- { behavior: 'smooth' }
475
- );
476
- },
477
- [rootElementRef, scrollTo]
478
- );
479
-
480
- // Since there could be multiple instances of <BasicTranscript> inside the <Composer>, when the developer calls `scrollXXX`, we need to call it on all instances.
481
- // We call `useRegisterScrollXXX` to register a callback function, the `useScrollXXX` will multiplex the call into each instance of <BasicTranscript>.
482
- useRegisterScrollTo(scrollTo);
483
- useRegisterScrollToEnd(scrollToEnd);
484
- useRegisterScrollRelative(scrollRelative);
485
-
486
- const dispatchScrollPosition = useDispatchScrollPosition();
487
- const patchedDispatchScrollPosition = useMemo(() => {
488
- if (!dispatchScrollPosition) {
489
- return;
490
- }
491
-
492
- return ({ scrollTop }) => {
493
- const { current: rootElement } = rootElementRef;
494
-
495
- if (!rootElement) {
496
- return;
497
- }
498
-
499
- const scrollableElement = rootElement.querySelector('.webchat__basic-transcript__scrollable');
500
-
501
- const [{ height: offsetHeight } = {}] = scrollableElement.getClientRects();
502
-
503
- // Find the activity just above scroll view bottom.
504
- // If the scroll view is already on top, get the first activity.
505
- const entry = scrollableElement.scrollTop
506
- ? [...activityElementsRef.current].reverse().find(({ element }) => {
507
- if (!element) {
508
- return false;
509
- }
510
-
511
- const [{ y } = {}] = element.getClientRects();
512
-
513
- return y < offsetHeight;
514
- })
515
- : activityElementsRef.current[0];
516
-
517
- const { activityID } = entry || {};
518
-
519
- dispatchScrollPosition({ ...(activityID ? { activityID } : {}), scrollTop });
520
- };
521
- }, [activityElementsRef, dispatchScrollPosition, rootElementRef]);
522
-
523
- useObserveScrollPosition(patchedDispatchScrollPosition);
524
-
525
- const [lastInteractedActivity] = useAcknowledgedActivity();
526
-
527
- const indexOfLastInteractedActivity = activities.indexOf(lastInteractedActivity);
528
-
529
- // Create a new ID for aria-activedescendant every time the active descendant change.
530
- // In our design, the transcript will only have 1 focused activity and it has an ID. Other blurred activities will not have ID assigned.
531
- // This help with performance.
532
- // But browser usually do noop if the value of aria-activedescendant doesn't change.
533
- // That means, if we assign the same ID to another element, browser will do noop.
534
- // We need to generate a new ID so the browser see there is a change in aria-activedescendant value and perform accordingly.
535
- const activeDescendantElementId = useMemo(
536
- () => focusedActivityKey && `webchat__basic-transcript__active-descendant-${random().toString(36).substr(2, 5)}`,
537
- [focusedActivityKey]
538
- );
539
-
540
- const scrollActiveDescendantIntoView = useCallback(() => {
541
- const activeDescendant = activeDescendantElementId && document.getElementById(activeDescendantElementId);
542
-
543
- // Don't scroll active descendant into view if the focus is already inside it.
544
- // Otherwise, given the focus is on the send box, clicking on any <input> inside the Adaptive Cards may cause the view to move.
545
- // This UX is not desirable because click should not cause scroll.
546
- if (activeDescendant && !activeDescendant.contains(document.activeElement)) {
547
- // Checks if scrollIntoView support options or not.
548
- // - https://github.com/Modernizr/Modernizr/issues/1568#issuecomment-419457972
549
- // - https://stackoverflow.com/questions/46919627/is-it-possible-to-test-for-scrollintoview-browser-compatibility
550
- if ('scrollBehavior' in document.documentElement.style) {
551
- activeDescendant.scrollIntoView({ block: 'nearest' });
552
- } else {
553
- // This is for browser that does not support options passed to scrollIntoView(), possibly IE11.
554
- const scrollableElement = rootElementRef.current.querySelector('.webchat__basic-transcript__scrollable');
555
- const scrollTopAtTopSide = activeDescendant.offsetTop;
556
- const scrollTopAtBottomSide = activeDescendant.offsetTop + activeDescendant.offsetHeight;
557
-
558
- if (scrollTopAtTopSide < scrollableElement.scrollTop) {
559
- scrollableElement.scrollTop = scrollTopAtTopSide;
560
- } else if (scrollTopAtBottomSide > scrollableElement.scrollTop + scrollableElement.offsetHeight) {
561
- scrollableElement.scrollTop = scrollTopAtBottomSide - scrollableElement.offsetHeight;
562
- }
563
- }
564
- }
565
- }, [activeDescendantElementId, rootElementRef]);
566
-
567
- const handleTranscriptFocus = useCallback(
568
- event => {
569
- const { currentTarget, target } = event;
570
-
571
- // When focus is placed on the transcript, scroll active descendant into the view.
572
- currentTarget === target && scrollActiveDescendantIntoView();
573
- },
574
- [scrollActiveDescendantIntoView]
575
- );
576
-
577
- // After new aria-activedescendant is assigned, we will need to scroll it into view.
578
- // User agent will scroll automatically for focusing element, but not for aria-activedescendant.
579
- // We need to do the scrolling manually.
580
- useEffect(() => scrollActiveDescendantIntoView(), [scrollActiveDescendantIntoView]);
581
-
582
- // If any activities has changed, reset the active descendant.
583
- useEffect(() => setFocusedActivityKey(undefined), [activities, setFocusedActivityKey]);
584
-
585
- const focusRelativeActivity = useCallback(
586
- delta => {
587
- if (isNaN(delta) || !renderingElements.length) {
588
- return setFocusedActivityKey(undefined);
589
- }
590
-
591
- const index = renderingElements.findIndex(({ key }) => key === focusedActivityKey);
592
- const nextIndex = ~index
593
- ? Math.max(0, Math.min(renderingElements.length - 1, index + delta))
594
- : renderingElements.length - 1;
595
- const nextFocusedActivity = renderingElements[nextIndex];
596
-
597
- setFocusedActivityKey(nextFocusedActivity.key);
598
- rootElementRef.current && rootElementRef.current.focus();
599
- },
600
- [focusedActivityKey, renderingElements, rootElementRef, setFocusedActivityKey]
601
- );
602
-
603
- const handleTranscriptKeyDown = useCallback(
604
- event => {
605
- const { target } = event;
606
-
607
- const fromEndOfTranscriptIndicator = target === terminatorRef.current;
608
- const fromTranscript = target === event.currentTarget;
609
-
610
- if (!fromEndOfTranscriptIndicator && !fromTranscript) {
611
- return;
612
- }
613
-
614
- let handled = true;
615
-
616
- switch (event.key) {
617
- case 'ArrowDown':
618
- focusRelativeActivity(fromEndOfTranscriptIndicator ? 0 : 1);
619
- break;
620
-
621
- case 'ArrowUp':
622
- focusRelativeActivity(fromEndOfTranscriptIndicator ? 0 : -1);
623
- break;
624
-
625
- case 'End':
626
- focusRelativeActivity(Infinity);
627
- break;
628
-
629
- case 'Enter':
630
- if (!fromEndOfTranscriptIndicator) {
631
- const focusedActivityEntry = renderingElements.find(({ key }) => key === focusedActivityKey);
632
-
633
- if (focusedActivityEntry) {
634
- const { element: focusedActivityElement } =
635
- activityElementsRef.current.find(({ activity }) => activity === focusedActivityEntry.activity) || {};
636
-
637
- if (focusedActivityElement) {
638
- const [firstTabbableElement] = tabbableElements(focusedActivityElement).filter(
639
- ({ className }) => className !== 'webchat__basic-transcript__activity-sentinel'
640
- );
641
-
642
- firstTabbableElement && firstTabbableElement.focus();
643
- }
644
- }
645
- }
646
-
647
- break;
648
-
649
- case 'Escape':
650
- focus('sendBoxWithoutKeyboard');
651
- break;
652
-
653
- case 'Home':
654
- focusRelativeActivity(-Infinity);
655
- break;
656
-
657
- default:
658
- handled = false;
659
- break;
660
- }
661
-
662
- if (handled) {
663
- event.preventDefault();
664
-
665
- // If a custom HTML control wants to handle up/down arrow, we will prevent them from listening to this event to prevent bugs due to handling arrow keys twice.
666
- event.stopPropagation();
667
- }
668
- },
669
- [focusedActivityKey, activityElementsRef, focusRelativeActivity, focus, terminatorRef, renderingElements]
670
- );
671
-
672
- const labelId = useUniqueId('webchat__basic-transcript__label');
673
-
674
- // If SHIFT-TAB from "End of transcript" indicator, if focusedActivityKey is not set (or no longer exists), set it the the bottommost activity.
675
- const setBottommostFocusedActivityKeyIfNeeded = useCallback(() => {
676
- if (!~renderingElements.findIndex(({ key }) => key === focusedActivityKey)) {
677
- const { key: lastActivityKey } = renderingElements[renderingElements.length - 1] || {};
678
-
679
- setFocusedActivityKey(lastActivityKey);
680
- }
681
- }, [focusedActivityKey, renderingElements, setFocusedActivityKey]);
682
-
683
- const handleTranscriptKeyDownCapture = useCallback(
684
- event => {
685
- const { altKey, ctrlKey, key, metaKey, target } = event;
686
-
687
- if (altKey || (ctrlKey && key !== 'v') || metaKey || (!inputtableKey(key) && key !== 'Backspace')) {
688
- // Ignore if one of the utility key (except SHIFT) is pressed
689
- // E.g. CTRL-C on a link in one of the message should not jump to chat box
690
- // E.g. "A" or "Backspace" should jump to chat box
691
- return;
692
- }
693
-
694
- // Send keystrokes to send box if we are focusing on the transcript or terminator.
695
- if (target === event.currentTarget || target === terminatorRef.current) {
696
- event.stopPropagation();
697
-
698
- focus('sendBox');
699
- }
700
- },
701
- [focus]
702
- );
703
-
704
- const focusTranscriptCallback = useCallback(
705
- () => rootElementRef.current && rootElementRef.current.focus(),
706
- [rootElementRef]
707
- );
708
-
709
- useRegisterFocusTranscript(focusTranscriptCallback);
710
-
711
- const handleFocusActivity = useCallback(
712
- key => {
713
- setFocusedActivityKey(key);
714
- rootElementRef.current && rootElementRef.current.focus();
715
- },
716
- [setFocusedActivityKey]
717
- );
718
-
719
- // When the focusing activity has changed, dispatch an event to observers of "useObserveTranscriptFocus".
720
- const dispatchTranscriptFocus = useDispatchTranscriptFocus();
721
- const focusedActivity = useMemo(() => {
722
- const { activity } = renderingElements.find(({ key }) => key === focusedActivityKey) || {};
723
-
724
- return activity;
725
- }, [focusedActivityKey, renderingElements]);
726
-
727
- useMemo(
728
- () => dispatchTranscriptFocus && dispatchTranscriptFocus({ activity: focusedActivity }),
729
- [dispatchTranscriptFocus, focusedActivity]
730
- );
731
-
732
- // This is required by IE11.
733
- // When the user clicks on and empty space (a.k.a. filler) in an empty transcript, IE11 says the focus is on the <div className="filler">,
734
- // despite the fact there are no "tabIndex" attributes set on the filler.
735
- // We need to artificially send the focus back to the transcript.
736
- const handleFocusFiller = useCallback(() => {
737
- const { current } = rootElementRef;
738
-
739
- current && current.focus();
740
- }, [rootElementRef]);
741
-
742
- return (
743
- <div
744
- aria-activedescendant={focusedActivityKey ? activeDescendantElementId : undefined}
745
- aria-labelledby={labelId}
746
- className={classNames(
747
- 'webchat__basic-transcript',
748
- basicTranscriptStyleSet + '',
749
- rootClassName,
750
- (className || '') + ''
751
- )}
752
- dir={direction}
753
- onFocus={handleTranscriptFocus}
754
- onKeyDown={handleTranscriptKeyDown}
755
- onKeyDownCapture={handleTranscriptKeyDownCapture}
756
- ref={rootElementRef}
757
- // "aria-activedescendant" will only works with a number of roles and it must be explicitly set.
758
- // https://www.w3.org/TR/wai-aria/#aria-activedescendant
759
- role="group"
760
- // For up/down arrow key navigation across activities, this component must be included in the tab sequence.
761
- // Otherwise, "aria-activedescendant" will not be narrated when the user press up/down arrow keys.
762
- // https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_focus_activedescendant
763
- tabIndex={0}
764
- >
765
- <ScreenReaderText id={labelId} text={transcriptAriaLabel} />
766
- {/* This <section> is for live region only. Content is made invisible through CSS. */}
767
- <section
768
- aria-atomic={false}
769
- aria-live="polite"
770
- aria-relevant="additions"
771
- aria-roledescription={transcriptRoleDescription}
772
- role="log"
773
- >
774
- {renderingElements
775
- .filter(({ supportScreenReader }) => supportScreenReader)
776
- .map(({ activity, liveRegionKey }) => (
777
- <Fade fadeAfter={internalLiveRegionFadeAfter} key={liveRegionKey}>
778
- {() => <ScreenReaderActivity activity={activity} />}
779
- </Fade>
780
- ))}
781
- </section>
782
- {/* TODO: [P2] Fix ESLint error `no-use-before-define` */}
783
- {/* eslint-disable-next-line @typescript-eslint/no-use-before-define */}
784
- <InternalTranscriptScrollable
785
- activities={renderingActivities}
786
- onFocusActivity={handleFocusActivity}
787
- onFocusFiller={handleFocusFiller}
788
- terminatorRef={terminatorRef}
789
- >
790
- {renderingElements.map(
791
- (
792
- {
793
- activity,
794
- callbackRef,
795
- focusActivity,
796
- handleFocus,
797
- handleKeyDown,
798
- handleMouseDownCapture,
799
- hideTimestamp,
800
- key,
801
- renderActivity,
802
- renderActivityStatus,
803
- renderAvatar,
804
- role,
805
- shouldSpeak,
806
- showCallout,
807
- supportScreenReader
808
- },
809
- index
810
- ) => {
811
- const { ariaLabelID, element } =
812
- activityElementsRef.current.find(entry => entry.activity === activity) || {};
813
- const activeDescendant = focusedActivityKey === key;
814
- const isContentInteractive = !!(element
815
- ? tabbableElements(element.querySelector('.webchat__basic-transcript__activity-box')).length
816
- : 0);
817
-
818
- return (
819
- <li
820
- aria-labelledby={ariaLabelID}
821
- className={classNames('webchat__basic-transcript__activity', {
822
- 'webchat__basic-transcript__activity--acknowledged': index <= indexOfLastInteractedActivity,
823
- 'webchat__basic-transcript__activity--from-bot': role !== 'user',
824
- 'webchat__basic-transcript__activity--from-user': role === 'user'
825
- })}
826
- // Set "id" for valid for accessibility.
827
- /* eslint-disable-next-line react/forbid-dom-props */
828
- id={activeDescendant ? activeDescendantElementId : undefined}
829
- key={key}
830
- onFocus={handleFocus}
831
- onKeyDown={handleKeyDown}
832
- onMouseDownCapture={handleMouseDownCapture}
833
- ref={callbackRef}
834
- >
835
- {supportScreenReader && (
836
- <ScreenReaderActivity activity={activity} id={ariaLabelID} renderAttachments={false}>
837
- {!!isContentInteractive && <p>{activityInteractiveAlt}</p>}
838
- </ScreenReaderActivity>
839
- )}
840
- <FocusRedirector
841
- className="webchat__basic-transcript__activity-sentinel"
842
- onFocus={focusActivity}
843
- redirectRef={rootElementRef}
844
- />
845
- <div className="webchat__basic-transcript__activity-box">
846
- {renderActivity({
847
- hideTimestamp,
848
- renderActivityStatus,
849
- renderAvatar,
850
- showCallout
851
- })}
852
- </div>
853
- {shouldSpeak && <SpeakActivity activity={activity} />}
854
- <FocusRedirector
855
- className="webchat__basic-transcript__activity-sentinel"
856
- onFocus={focusActivity}
857
- redirectRef={rootElementRef}
858
- />
859
- <div
860
- className={classNames('webchat__basic-transcript__activity-indicator', {
861
- 'webchat__basic-transcript__activity-indicator--first': !index,
862
- 'webchat__basic-transcript__activity-indicator--focus': activeDescendant
863
- })}
864
- />
865
- </li>
866
- );
867
- }
868
- )}
869
- </InternalTranscriptScrollable>
870
- {!!renderingElements.length && (
871
- <React.Fragment>
872
- <FocusRedirector
873
- className="webchat__basic-transcript__sentinel"
874
- onFocus={setBottommostFocusedActivityKeyIfNeeded}
875
- redirectRef={rootElementRef}
876
- />
877
- <div className="webchat__basic-transcript__terminator" ref={terminatorRef} tabIndex={0}>
878
- <div className="webchat__basic-transcript__terminator-body">
879
- <div className="webchat__basic-transcript__terminator-text">{terminatorText}</div>
880
- </div>
881
- </div>
882
- </React.Fragment>
883
- )}
884
- <div className="webchat__basic-transcript__focus-indicator" />
885
- </div>
886
- );
887
- };
888
-
889
- InternalTranscript.defaultProps = {
890
- className: ''
891
- };
892
-
893
- InternalTranscript.propTypes = {
894
- activityElementsRef: PropTypes.shape({
895
- current: PropTypes.array.isRequired
896
- }).isRequired,
897
- className: PropTypes.string
898
- };
899
-
900
- const InternalScreenReaderTranscript = ({ renderingElements }) => {
901
- const localize = useLocalizer();
902
- const [internalLiveRegionFadeAfter] = useStyleOptions();
903
-
904
- const transcriptRoleDescription = localize('TRANSCRIPT_ARIA_ROLE_ALT');
905
-
906
- return (
907
- <section
908
- aria-atomic={false}
909
- aria-live="polite"
910
- aria-relevant="additions"
911
- aria-roledescription={transcriptRoleDescription}
912
- role="log"
913
- >
914
- {renderingElements.map(({ activity, liveRegionKey }) => (
915
- <Fade fadeAfter={internalLiveRegionFadeAfter} key={liveRegionKey}>
916
- {() => <ScreenReaderActivity activity={activity} />}
917
- </Fade>
918
- ))}
919
- </section>
920
- );
921
- };
922
-
923
- InternalScreenReaderTranscript.propTypes = {
924
- renderingElements: PropTypes.arrayOf(
925
- PropTypes.shape({
926
- activity: PropTypes.any,
927
- liveRegionKey: PropTypes.string
928
- })
929
- ).isRequired
930
- };
931
-
932
- // Separating high-frequency hooks to improve performance.
933
- const InternalTranscriptScrollable = ({ activities, children, onFocusActivity, onFocusFiller, terminatorRef }) => {
934
- const [{ activities: activitiesStyleSet }] = useStyleSet();
935
- const [animatingToEnd] = useAnimatingToEnd();
936
- const [atEnd] = useAtEnd();
937
- const [sticky] = useSticky();
938
- const [styleOptions] = useStyleOptions();
939
- const lastVisibleActivityId = getActivityUniqueId(activities[activities.length - 1] || {}); // Activity ID of the last visible activity in the list.
940
- const localize = useLocalizer();
941
- const scrollToEnd = useScrollToEnd();
942
-
943
- const lastReadActivityIdRef = useRef(lastVisibleActivityId);
944
- const transcriptRoleDescription = localize('TRANSCRIPT_ARIA_ROLE_ALT');
945
-
946
- const handleScrollToEndButtonClick = useCallback(() => {
947
- scrollToEnd({ behavior: 'smooth' });
948
-
949
- // After the "New message" button is clicked, focus on the first unread activity.
950
- const index = activities.findIndex(({ id }) => id === lastReadActivityIdRef.current);
951
-
952
- if (~index) {
953
- const firstUnreadActivity = activities[index + 1];
954
-
955
- if (firstUnreadActivity) {
956
- return onFocusActivity(getActivityUniqueId(firstUnreadActivity));
957
- }
958
- }
959
-
960
- const { current } = terminatorRef;
961
-
962
- current && current.focus();
963
- }, [activities, lastReadActivityIdRef, onFocusActivity, scrollToEnd, terminatorRef]);
964
-
965
- if (atEnd || sticky) {
966
- // If it is sticky or at the end, the user is at the bottom of the transcript, everything is read.
967
- // So mark the activity ID as read.
968
- lastReadActivityIdRef.current = lastVisibleActivityId;
969
- }
970
-
971
- const ScrollToEndButton = useCreateScrollToEndButtonRenderer()({
972
- atEnd: animatingToEnd || atEnd || sticky,
973
- styleOptions,
974
-
975
- // Unread means:
976
- // 1. Last read is not the last one in the transcript, and;
977
- // 2. Last read is still in the transcript.
978
- unread:
979
- lastVisibleActivityId !== lastReadActivityIdRef.current &&
980
- !!~activities.findIndex(activity => getActivityUniqueId(activity) === lastReadActivityIdRef.current)
981
- });
982
-
983
- return (
984
- <React.Fragment>
985
- {ScrollToEndButton && <ScrollToEndButton onClick={handleScrollToEndButtonClick} />}
986
- {!!React.Children.count(children) && (
987
- <FocusRedirector className="webchat__basic-transcript__sentinel" redirectRef={terminatorRef} />
988
- )}
989
- <ReactScrollToBottomPanel className="webchat__basic-transcript__scrollable">
990
- <div aria-hidden={true} className="webchat__basic-transcript__filler" onFocus={onFocusFiller} />
991
- <ul
992
- aria-roledescription={transcriptRoleDescription}
993
- className={classNames(activitiesStyleSet + '', 'webchat__basic-transcript__transcript')}
994
- role="list"
995
- >
996
- {children}
997
- </ul>
998
- <BasicTypingIndicator />
999
- </ReactScrollToBottomPanel>
1000
- </React.Fragment>
1001
- );
1002
- };
1003
-
1004
- InternalTranscriptScrollable.propTypes = {
1005
- activities: PropTypes.array.isRequired,
1006
- children: PropTypes.any.isRequired,
1007
- onFocusActivity: PropTypes.func.isRequired,
1008
- onFocusFiller: PropTypes.func.isRequired,
1009
- terminatorRef: PropTypes.any.isRequired
1010
- };
1011
-
1012
- const SetScroller = ({ activityElementsRef, scrollerRef }) => {
1013
- const [
1014
- { autoScrollSnapOnActivity, autoScrollSnapOnActivityOffset, autoScrollSnapOnPage, autoScrollSnapOnPageOffset }
1015
- ] = useStyleOptions();
1016
- const [activities] = useActivities();
1017
- const [lastAcknowledgedActivity] = useAcknowledgedActivity();
1018
-
1019
- const activitiesRef = useRef(activities);
1020
- const lastAcknowledgedActivityRef = useRef(lastAcknowledgedActivity);
1021
-
1022
- activitiesRef.current = activities;
1023
- lastAcknowledgedActivityRef.current = lastAcknowledgedActivity;
1024
-
1025
- scrollerRef.current = useCallback(
1026
- ({ offsetHeight, scrollTop }) => {
1027
- const patchedAutoScrollSnapOnActivity =
1028
- typeof autoScrollSnapOnActivity === 'number'
1029
- ? Math.max(0, autoScrollSnapOnActivity)
1030
- : autoScrollSnapOnActivity
1031
- ? 1
1032
- : 0;
1033
- const patchedAutoScrollSnapOnPage =
1034
- typeof autoScrollSnapOnPage === 'number'
1035
- ? Math.max(0, Math.min(1, autoScrollSnapOnPage))
1036
- : autoScrollSnapOnPage
1037
- ? 1
1038
- : 0;
1039
- const patchedAutoScrollSnapOnActivityOffset =
1040
- typeof autoScrollSnapOnActivityOffset === 'number' ? autoScrollSnapOnActivityOffset : 0;
1041
- const patchedAutoScrollSnapOnPageOffset =
1042
- typeof autoScrollSnapOnPageOffset === 'number' ? autoScrollSnapOnPageOffset : 0;
1043
-
1044
- if (patchedAutoScrollSnapOnActivity || patchedAutoScrollSnapOnPage) {
1045
- const { current: activities } = activitiesRef;
1046
- const { current: activityElements } = activityElementsRef;
1047
- const { current: lastAcknowledgedActivity } = lastAcknowledgedActivityRef;
1048
- const values = [];
1049
-
1050
- const lastAcknowledgedActivityIndex = activities.indexOf(lastAcknowledgedActivity);
1051
-
1052
- if (~lastAcknowledgedActivityIndex) {
1053
- // The activity that we acknowledged could be not rendered, such as post back activity.
1054
- // When calculating scroll snap, we can only base on the first unacknowledged-and-rendering activity.
1055
- let firstUnacknowledgedActivityElementIndex = -1;
1056
-
1057
- for (let index = lastAcknowledgedActivityIndex + 1, { length } = activities; index < length; index++) {
1058
- const activity = activities[index];
1059
- const activityElementIndex = activityElements.findIndex(entry => entry.activity === activity);
1060
-
1061
- if (~activityElementIndex) {
1062
- firstUnacknowledgedActivityElementIndex = activityElementIndex;
1063
- break;
1064
- }
1065
- }
1066
-
1067
- if (~firstUnacknowledgedActivityElementIndex) {
1068
- if (patchedAutoScrollSnapOnActivity) {
1069
- // Gets the activity element which we should snap to.
1070
- const { element: nthUnacknowledgedActivityElement } =
1071
- activityElements[firstUnacknowledgedActivityElementIndex + patchedAutoScrollSnapOnActivity - 1];
1072
-
1073
- if (nthUnacknowledgedActivityElement) {
1074
- values.push(
1075
- nthUnacknowledgedActivityElement.offsetTop +
1076
- nthUnacknowledgedActivityElement.offsetHeight -
1077
- offsetHeight -
1078
- scrollTop +
1079
- patchedAutoScrollSnapOnActivityOffset
1080
- );
1081
- }
1082
- }
1083
-
1084
- if (patchedAutoScrollSnapOnPage) {
1085
- const { element: firstUnacknowledgedActivityElement } =
1086
- activityElements[firstUnacknowledgedActivityElementIndex];
1087
-
1088
- values.push(
1089
- firstUnacknowledgedActivityElement.offsetTop -
1090
- scrollTop -
1091
- offsetHeight * (1 - patchedAutoScrollSnapOnPage) +
1092
- patchedAutoScrollSnapOnPageOffset
1093
- );
1094
- }
1095
- }
1096
- }
1097
-
1098
- return values.reduce((minValue, value) => Math.min(minValue, value), Infinity);
1099
- }
1100
-
1101
- return Infinity;
1102
- },
1103
- [
1104
- activitiesRef,
1105
- activityElementsRef,
1106
- autoScrollSnapOnActivity,
1107
- autoScrollSnapOnActivityOffset,
1108
- autoScrollSnapOnPage,
1109
- autoScrollSnapOnPageOffset,
1110
- lastAcknowledgedActivityRef
1111
- ]
1112
- );
1113
-
1114
- return false;
1115
- };
1116
-
1117
- const BasicTranscript = ({ className }) => {
1118
- const activityElementsRef = useRef([]);
1119
- const scrollerRef = useRef(() => Infinity);
1120
-
1121
- const scroller = useCallback((...args) => scrollerRef.current(...args), [scrollerRef]);
1122
-
1123
- return (
1124
- <ReactScrollToBottomComposer scroller={scroller}>
1125
- <SetScroller activityElementsRef={activityElementsRef} scrollerRef={scrollerRef} />
1126
- <InternalTranscript activityElementsRef={activityElementsRef} className={className} />
1127
- </ReactScrollToBottomComposer>
1128
- );
1129
- };
1130
-
1131
- BasicTranscript.defaultProps = {
1132
- className: ''
1133
- };
1134
-
1135
- BasicTranscript.propTypes = {
1136
- className: PropTypes.string
1137
- };
1138
-
1139
- export default BasicTranscript;