botframework-webchat-component 4.13.0 → 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.
- package/.eslintrc.yml +4 -100
- package/.prettierrc.yml +1 -1
- package/lib/Activity/Avatar.d.ts +10 -0
- package/lib/Activity/Avatar.d.ts.map +1 -0
- package/lib/Activity/Avatar.js +2 -1
- package/lib/Activity/Bubble.d.ts +11 -0
- package/lib/Activity/Bubble.d.ts.map +1 -0
- package/lib/Activity/Bubble.js +2 -2
- package/lib/Activity/CarouselFilmStrip.js +21 -31
- package/lib/Activity/CarouselFilmStripAttachment.js +120 -0
- package/lib/Activity/CarouselLayout.js +5 -5
- package/lib/Activity/Speak.d.ts +10 -0
- package/lib/Activity/Speak.d.ts.map +1 -0
- package/lib/Activity/Speak.js +11 -13
- package/lib/Activity/StackedLayout.d.ts +18 -0
- package/lib/Activity/StackedLayout.d.ts.map +1 -0
- package/lib/Activity/StackedLayout.js +19 -15
- package/lib/Assets/TypingAnimation.js +2 -2
- package/lib/Attachment/Assets/DownloadIcon.js +3 -1
- package/lib/Attachment/AudioAttachment.js +2 -2
- package/lib/Attachment/AudioContent.d.ts +11 -0
- package/lib/Attachment/AudioContent.d.ts.map +1 -0
- package/lib/Attachment/AudioContent.js +4 -4
- package/lib/Attachment/FileAttachment.js +2 -2
- package/lib/Attachment/FileContent.d.ts +10 -0
- package/lib/Attachment/FileContent.d.ts.map +1 -0
- package/lib/Attachment/FileContent.js +14 -9
- package/lib/Attachment/HTMLVideoContent.d.ts +11 -0
- package/lib/Attachment/HTMLVideoContent.d.ts.map +1 -0
- package/lib/Attachment/HTMLVideoContent.js +2 -2
- package/lib/Attachment/ImageContent.d.ts +8 -0
- package/lib/Attachment/ImageContent.d.ts.map +1 -0
- package/lib/Attachment/ImageContent.js +2 -2
- package/lib/Attachment/TextContent.d.ts +8 -0
- package/lib/Attachment/TextContent.d.ts.map +1 -0
- package/lib/Attachment/TextContent.js +4 -4
- package/lib/Attachment/VideoAttachment.js +2 -2
- package/lib/Attachment/VideoContent.d.ts +11 -0
- package/lib/Attachment/VideoContent.d.ts.map +1 -0
- package/lib/Attachment/VideoContent.js +1 -1
- package/lib/Attachment/VimeoContent.d.ts +10 -0
- package/lib/Attachment/VimeoContent.d.ts.map +1 -0
- package/lib/Attachment/VimeoContent.js +8 -8
- package/lib/Attachment/YouTubeContent.d.ts +10 -0
- package/lib/Attachment/YouTubeContent.d.ts.map +1 -0
- package/lib/Attachment/YouTubeContent.js +5 -5
- package/lib/Avatar/ImageAvatar.js +2 -2
- package/lib/Avatar/InitialsAvatar.js +2 -2
- package/lib/BasicConnectivityStatus.js +2 -2
- package/lib/BasicSendBox.d.ts +9 -0
- package/lib/BasicSendBox.d.ts.map +1 -0
- package/lib/BasicSendBox.js +7 -6
- package/lib/BasicToast.js +4 -4
- package/lib/BasicToaster.js +8 -5
- package/lib/BasicTranscript.d.ts +7 -0
- package/lib/BasicTranscript.d.ts.map +1 -0
- package/lib/BasicTranscript.js +406 -743
- package/lib/BasicTypingIndicator.d.ts +6 -0
- package/lib/BasicTypingIndicator.d.ts.map +1 -0
- package/lib/BasicTypingIndicator.js +13 -5
- package/lib/BasicWebChat.d.ts +9 -0
- package/lib/BasicWebChat.d.ts.map +1 -0
- package/lib/BasicWebChat.js +6 -4
- package/lib/Composer.d.ts +23 -0
- package/lib/Composer.d.ts.map +1 -0
- package/lib/Composer.js +65 -82
- package/lib/ConnectivityStatus/Assets/ErrorNotificationIcon.js +2 -2
- package/lib/ConnectivityStatus/Assets/SpinnerAnimation.js +2 -2
- package/lib/ConnectivityStatus/Assets/WarningNotificationIcon.js +2 -2
- package/lib/ConnectivityStatus/Connected.js +1 -1
- package/lib/ConnectivityStatus/Connecting.js +4 -4
- package/lib/ConnectivityStatus/FailedToConnect.js +2 -2
- package/lib/ConnectivityStatus/JavaScriptError.js +2 -2
- package/lib/Dictation.js +13 -4
- package/lib/ErrorBox.d.ts +8 -0
- package/lib/ErrorBox.d.ts.map +1 -0
- package/lib/ErrorBox.js +2 -2
- package/lib/Middleware/Activity/createCoreMiddleware.d.ts +3 -0
- package/lib/Middleware/Activity/createCoreMiddleware.d.ts.map +1 -0
- package/lib/Middleware/Activity/createCoreMiddleware.js +1 -1
- package/lib/Middleware/ActivityStatus/AbsoluteTime.js +1 -1
- package/lib/Middleware/ActivityStatus/RelativeTime.js +1 -1
- package/lib/Middleware/ActivityStatus/SendStatus/SendFailedRetry.js +3 -3
- package/lib/Middleware/ActivityStatus/SendStatus/SendStatus.d.ts +11 -0
- package/lib/Middleware/ActivityStatus/SendStatus/SendStatus.d.ts.map +1 -0
- package/lib/Middleware/ActivityStatus/SendStatus/SendStatus.js +5 -5
- package/lib/Middleware/ActivityStatus/Timestamp.d.ts +9 -0
- package/lib/Middleware/ActivityStatus/Timestamp.d.ts.map +1 -0
- package/lib/Middleware/ActivityStatus/Timestamp.js +2 -2
- package/lib/Middleware/ActivityStatus/createCoreMiddleware.d.ts +3 -0
- package/lib/Middleware/ActivityStatus/createCoreMiddleware.d.ts.map +1 -0
- package/lib/Middleware/ActivityStatus/createCoreMiddleware.js +1 -1
- package/lib/Middleware/ActivityStatus/createSendStatusMiddleware.js +21 -16
- package/lib/Middleware/ActivityStatus/createTimestampMiddleware.js +1 -1
- package/lib/Middleware/Attachment/createCoreMiddleware.d.ts +3 -0
- package/lib/Middleware/Attachment/createCoreMiddleware.d.ts.map +1 -0
- package/lib/Middleware/Attachment/createCoreMiddleware.js +10 -13
- package/lib/Middleware/AttachmentForScreenReader/AudioAttachment.js +1 -1
- package/lib/Middleware/AttachmentForScreenReader/FileAttachment.js +1 -1
- package/lib/Middleware/AttachmentForScreenReader/ImageAttachment.js +1 -1
- package/lib/Middleware/AttachmentForScreenReader/TextAttachment.js +1 -1
- package/lib/Middleware/AttachmentForScreenReader/VideoAttachment.js +1 -1
- package/lib/Middleware/AttachmentForScreenReader/createCoreMiddleware.d.ts +3 -0
- package/lib/Middleware/AttachmentForScreenReader/createCoreMiddleware.d.ts.map +1 -0
- package/lib/Middleware/AttachmentForScreenReader/createCoreMiddleware.js +9 -5
- package/lib/Middleware/Avatar/createCoreMiddleware.d.ts +11 -0
- package/lib/Middleware/Avatar/createCoreMiddleware.d.ts.map +1 -0
- package/lib/Middleware/Avatar/createCoreMiddleware.js +3 -4
- package/lib/Middleware/CardAction/createCoreMiddleware.js +11 -4
- package/lib/Middleware/GroupActivities/createCoreMiddleware.js +1 -1
- package/lib/Middleware/ScrollToEndButton/ScrollToEndButton.js +71 -0
- package/lib/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.d.ts +3 -0
- package/lib/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.d.ts.map +1 -0
- package/lib/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.js +27 -0
- package/lib/Middleware/Toast/createCoreMiddleware.d.ts +4 -0
- package/lib/Middleware/Toast/createCoreMiddleware.d.ts.map +1 -0
- package/lib/Middleware/Toast/createCoreMiddleware.js +1 -1
- package/lib/Middleware/TypingIndicator/createCoreMiddleware.d.ts +3 -0
- package/lib/Middleware/TypingIndicator/createCoreMiddleware.d.ts.map +1 -0
- package/lib/Middleware/TypingIndicator/createCoreMiddleware.js +9 -6
- package/lib/ReactWebChat.d.ts +10 -0
- package/lib/ReactWebChat.d.ts.map +1 -0
- package/lib/ReactWebChat.js +19 -7
- package/lib/ScreenReaderActivity.js +76 -48
- package/lib/ScreenReaderText.d.ts +9 -0
- package/lib/ScreenReaderText.d.ts.map +1 -0
- package/lib/ScreenReaderText.js +22 -8
- package/lib/SendBox/Assets/AttachmentIcon.js +3 -1
- package/lib/SendBox/Assets/MicrophoneIcon.js +3 -1
- package/lib/SendBox/Assets/SendIcon.js +2 -2
- package/lib/SendBox/AutoResizeTextArea.d.ts +23 -0
- package/lib/SendBox/AutoResizeTextArea.d.ts.map +1 -0
- package/lib/SendBox/AutoResizeTextArea.js +6 -6
- package/lib/SendBox/DictationInterims.d.ts +9 -0
- package/lib/SendBox/DictationInterims.d.ts.map +1 -0
- package/lib/SendBox/DictationInterims.js +3 -3
- package/lib/SendBox/IconButton.d.ts +11 -0
- package/lib/SendBox/IconButton.d.ts.map +1 -0
- package/lib/SendBox/IconButton.js +27 -7
- package/lib/SendBox/MicrophoneButton.d.ts +11 -0
- package/lib/SendBox/MicrophoneButton.d.ts.map +1 -0
- package/lib/SendBox/MicrophoneButton.js +25 -24
- package/lib/SendBox/SendButton.d.ts +9 -0
- package/lib/SendBox/SendButton.d.ts.map +1 -0
- package/lib/SendBox/SendButton.js +8 -6
- package/lib/SendBox/SuggestedAction.d.ts +17 -0
- package/lib/SendBox/SuggestedAction.d.ts.map +1 -0
- package/lib/SendBox/SuggestedAction.js +29 -24
- package/lib/SendBox/SuggestedActions.d.ts +5 -0
- package/lib/SendBox/SuggestedActions.d.ts.map +1 -0
- package/lib/SendBox/SuggestedActions.js +33 -13
- package/lib/SendBox/TextBox.d.ts +28 -0
- package/lib/SendBox/TextBox.d.ts.map +1 -0
- package/lib/SendBox/TextBox.js +13 -8
- package/lib/SendBox/UploadButton.d.ts +9 -0
- package/lib/SendBox/UploadButton.d.ts.map +1 -0
- package/lib/SendBox/UploadButton.js +6 -6
- package/lib/Styles/StyleSet/Activities.d.ts +5 -0
- package/lib/Styles/StyleSet/Activities.d.ts.map +1 -0
- package/lib/Styles/StyleSet/AudioAttachment.d.ts +6 -0
- package/lib/Styles/StyleSet/AudioAttachment.d.ts.map +1 -0
- package/lib/Styles/StyleSet/AudioContent.d.ts +4 -0
- package/lib/Styles/StyleSet/AudioContent.d.ts.map +1 -0
- package/lib/Styles/StyleSet/AutoResizeTextArea.d.ts +37 -0
- package/lib/Styles/StyleSet/AutoResizeTextArea.d.ts.map +1 -0
- package/lib/Styles/StyleSet/Avatar.d.ts +9 -0
- package/lib/Styles/StyleSet/Avatar.d.ts.map +1 -0
- package/lib/Styles/StyleSet/BasicTranscript.d.ts +85 -0
- package/lib/Styles/StyleSet/BasicTranscript.d.ts.map +1 -0
- package/lib/Styles/StyleSet/BasicTranscript.js +21 -9
- package/lib/Styles/StyleSet/Bubble.d.ts +5 -0
- package/lib/Styles/StyleSet/Bubble.d.ts.map +1 -0
- package/lib/Styles/StyleSet/Bubble.js +2 -2
- package/lib/Styles/StyleSet/CarouselFilmStrip.d.ts +5 -0
- package/lib/Styles/StyleSet/CarouselFilmStrip.d.ts.map +1 -0
- package/lib/Styles/StyleSet/CarouselFilmStrip.js +2 -18
- package/lib/Styles/StyleSet/CarouselFilmStripAttachment.d.ts +3 -0
- package/lib/Styles/StyleSet/CarouselFilmStripAttachment.d.ts.map +1 -0
- package/lib/Styles/StyleSet/CarouselFilmStripAttachment.js +63 -0
- package/lib/Styles/StyleSet/CarouselFlipper.d.ts +27 -0
- package/lib/Styles/StyleSet/CarouselFlipper.d.ts.map +1 -0
- package/lib/Styles/StyleSet/ConnectivityNotification.d.ts +13 -0
- package/lib/Styles/StyleSet/ConnectivityNotification.d.ts.map +1 -0
- package/lib/Styles/StyleSet/DictationInterims.d.ts +13 -0
- package/lib/Styles/StyleSet/DictationInterims.d.ts.map +1 -0
- package/lib/Styles/StyleSet/ErrorBox.d.ts +33 -0
- package/lib/Styles/StyleSet/ErrorBox.d.ts.map +1 -0
- package/lib/Styles/StyleSet/ErrorNotification.d.ts +23 -0
- package/lib/Styles/StyleSet/ErrorNotification.d.ts.map +1 -0
- package/lib/Styles/StyleSet/FileContent.d.ts +32 -0
- package/lib/Styles/StyleSet/FileContent.d.ts.map +1 -0
- package/lib/Styles/StyleSet/ImageAvatar.d.ts +7 -0
- package/lib/Styles/StyleSet/ImageAvatar.d.ts.map +1 -0
- package/lib/Styles/StyleSet/InitialsAvatar.d.ts +19 -0
- package/lib/Styles/StyleSet/InitialsAvatar.d.ts.map +1 -0
- package/lib/Styles/StyleSet/KeyboardHelp.d.ts +123 -0
- package/lib/Styles/StyleSet/KeyboardHelp.d.ts.map +1 -0
- package/lib/Styles/StyleSet/KeyboardHelp.js +145 -0
- package/lib/Styles/StyleSet/MicrophoneButton.d.ts +13 -0
- package/lib/Styles/StyleSet/MicrophoneButton.d.ts.map +1 -0
- package/lib/Styles/StyleSet/Root.d.ts +8 -0
- package/lib/Styles/StyleSet/Root.d.ts.map +1 -0
- package/lib/Styles/StyleSet/ScrollToEndButton.d.ts +36 -0
- package/lib/Styles/StyleSet/ScrollToEndButton.d.ts.map +1 -0
- package/lib/Styles/StyleSet/ScrollToEndButton.js +36 -34
- package/lib/Styles/StyleSet/SendBox.d.ts +24 -0
- package/lib/Styles/StyleSet/SendBox.d.ts.map +1 -0
- package/lib/Styles/StyleSet/SendBoxButton.d.ts +79 -0
- package/lib/Styles/StyleSet/SendBoxButton.d.ts.map +1 -0
- package/lib/Styles/StyleSet/SendBoxButton.js +76 -12
- package/lib/Styles/StyleSet/SendBoxTextBox.d.ts +41 -0
- package/lib/Styles/StyleSet/SendBoxTextBox.d.ts.map +1 -0
- package/lib/Styles/StyleSet/SendBoxTextBox.js +3 -3
- package/lib/Styles/StyleSet/SendStatus.d.ts +8 -0
- package/lib/Styles/StyleSet/SendStatus.d.ts.map +1 -0
- package/lib/Styles/StyleSet/SingleAttachmentActivity.d.ts +11 -0
- package/lib/Styles/StyleSet/SingleAttachmentActivity.d.ts.map +1 -0
- package/lib/Styles/StyleSet/SpinnerAnimation.d.ts +15 -0
- package/lib/Styles/StyleSet/SpinnerAnimation.d.ts.map +1 -0
- package/lib/Styles/StyleSet/StackedLayout.d.ts +63 -0
- package/lib/Styles/StyleSet/StackedLayout.d.ts.map +1 -0
- package/lib/Styles/StyleSet/StackedLayout.js +1 -1
- package/lib/Styles/StyleSet/SuggestedAction.d.ts +94 -0
- package/lib/Styles/StyleSet/SuggestedAction.d.ts.map +1 -0
- package/lib/Styles/StyleSet/SuggestedAction.js +116 -41
- package/lib/Styles/StyleSet/SuggestedActions.d.ts +125 -0
- package/lib/Styles/StyleSet/SuggestedActions.d.ts.map +1 -0
- package/lib/Styles/StyleSet/SuggestedActions.js +13 -6
- package/lib/Styles/StyleSet/TextContent.d.ts +28 -0
- package/lib/Styles/StyleSet/TextContent.d.ts.map +1 -0
- package/lib/Styles/StyleSet/Toast.d.ts +65 -0
- package/lib/Styles/StyleSet/Toast.d.ts.map +1 -0
- package/lib/Styles/StyleSet/Toaster.d.ts +111 -0
- package/lib/Styles/StyleSet/Toaster.d.ts.map +1 -0
- package/lib/Styles/StyleSet/TypingAnimation.d.ts +8 -0
- package/lib/Styles/StyleSet/TypingAnimation.d.ts.map +1 -0
- package/lib/Styles/StyleSet/TypingIndicator.d.ts +11 -0
- package/lib/Styles/StyleSet/TypingIndicator.d.ts.map +1 -0
- package/lib/Styles/StyleSet/UploadButton.d.ts +14 -0
- package/lib/Styles/StyleSet/UploadButton.d.ts.map +1 -0
- package/lib/Styles/StyleSet/VideoAttachment.d.ts +2 -0
- package/lib/Styles/StyleSet/VideoAttachment.d.ts.map +1 -0
- package/lib/Styles/StyleSet/VideoContent.d.ts +6 -0
- package/lib/Styles/StyleSet/VideoContent.d.ts.map +1 -0
- package/lib/Styles/StyleSet/VimeoContent.d.ts +7 -0
- package/lib/Styles/StyleSet/VimeoContent.d.ts.map +1 -0
- package/lib/Styles/StyleSet/WarningNotification.d.ts +22 -0
- package/lib/Styles/StyleSet/WarningNotification.d.ts.map +1 -0
- package/lib/Styles/StyleSet/YouTubeContent.d.ts +7 -0
- package/lib/Styles/StyleSet/YouTubeContent.d.ts.map +1 -0
- package/lib/Styles/createStyleSet.d.ts +1372 -0
- package/lib/Styles/createStyleSet.d.ts.map +1 -0
- package/lib/Styles/createStyleSet.js +47 -41
- package/lib/Styles/mirrorStyle.js +13 -4
- package/lib/Toast/CollapseIcon.js +3 -1
- package/lib/Toast/DismissIcon.js +3 -1
- package/lib/Toast/ExpandIcon.js +3 -1
- package/lib/Toast/NotificationIcon.js +7 -3
- package/lib/Toast/createToastMiddleware.d.ts +4 -0
- package/lib/Toast/createToastMiddleware.d.ts.map +1 -0
- package/lib/Toast/createToastMiddleware.js +2 -2
- package/lib/Transcript/ActivityRow.d.ts +9 -0
- package/lib/Transcript/ActivityRow.d.ts.map +1 -0
- package/lib/Transcript/ActivityRow.js +157 -0
- package/lib/Transcript/ActivityTextAlt.js +57 -0
- package/lib/Transcript/FocusTrap.d.ts +8 -0
- package/lib/Transcript/FocusTrap.d.ts.map +1 -0
- package/lib/Transcript/FocusTrap.js +74 -0
- package/lib/Transcript/KeyboardHelp.d.ts +4 -0
- package/lib/Transcript/KeyboardHelp.d.ts.map +1 -0
- package/lib/Transcript/KeyboardHelp.js +550 -0
- package/lib/Transcript/LiveRegionTranscript.d.ts +8 -0
- package/lib/Transcript/LiveRegionTranscript.d.ts.map +1 -0
- package/lib/Transcript/LiveRegionTranscript.js +214 -0
- package/lib/Transcript/types.d.ts +2 -0
- package/lib/Transcript/types.d.ts.map +1 -0
- package/lib/Transcript/types.js +2 -0
- package/lib/Transcript/useActivityAccessibleName.d.ts +4 -0
- package/lib/Transcript/useActivityAccessibleName.d.ts.map +1 -0
- package/lib/Transcript/useActivityAccessibleName.js +97 -0
- package/lib/Utils/AccessKeySink/Surface.js +7 -5
- package/lib/Utils/AccessibleButton.d.ts +11 -0
- package/lib/Utils/AccessibleButton.d.ts.map +1 -0
- package/lib/Utils/AccessibleButton.js +15 -8
- package/lib/Utils/AccessibleInputText.d.ts +21 -0
- package/lib/Utils/AccessibleInputText.d.ts.map +1 -0
- package/lib/Utils/AccessibleInputText.js +29 -25
- package/lib/Utils/AccessibleTextArea.d.ts +20 -0
- package/lib/Utils/AccessibleTextArea.d.ts.map +1 -0
- package/lib/Utils/AccessibleTextArea.js +35 -24
- package/lib/Utils/CroppedImage.js +2 -2
- package/lib/Utils/Fade.js +2 -2
- package/lib/Utils/FocusRedirector.d.ts +9 -0
- package/lib/Utils/FocusRedirector.d.ts.map +1 -0
- package/lib/Utils/FocusRedirector.js +18 -15
- package/lib/Utils/InlineMarkdown.js +17 -10
- package/lib/Utils/TypeFocusSink/FocusBox.js +6 -4
- package/lib/Utils/TypeFocusSink/getTabIndex.d.ts +2 -0
- package/lib/Utils/TypeFocusSink/getTabIndex.d.ts.map +1 -0
- package/lib/Utils/TypeFocusSink/getTabIndex.js +1 -1
- package/lib/Utils/TypeFocusSink/inputtableKey.d.ts +2 -0
- package/lib/Utils/TypeFocusSink/inputtableKey.d.ts.map +1 -0
- package/lib/Utils/TypeFocusSink/inputtableKey.js +5 -2
- package/lib/Utils/TypeFocusSink/navigableEvent.js +1 -1
- package/lib/Utils/activityAltText.d.ts +8 -0
- package/lib/Utils/activityAltText.d.ts.map +1 -0
- package/lib/Utils/activityAltText.js +100 -0
- package/lib/Utils/addTargetBlankToHyperlinksMarkdown.js +1 -1
- package/lib/Utils/createCustomEvent.js +9 -3
- package/lib/Utils/debounce.js +1 -1
- package/lib/Utils/detectBrowser.js +4 -2
- package/lib/Utils/downscaleImageToDataURL/downscaleImageToDataURLUsingWorker.js +3 -3
- package/lib/Utils/downscaleImageToDataURL/index.js +2 -2
- package/lib/Utils/filterMap.js +1 -1
- package/lib/Utils/findAncestor.js +17 -0
- package/lib/Utils/getActivityUniqueId.js +4 -2
- package/lib/Utils/intersectionOf.d.ts +5 -0
- package/lib/Utils/intersectionOf.d.ts.map +1 -0
- package/lib/Utils/intersectionOf.js +17 -2
- package/lib/Utils/isZeroOrPositive.d.ts +5 -0
- package/lib/Utils/isZeroOrPositive.d.ts.map +1 -0
- package/lib/Utils/isZeroOrPositive.js +4 -1
- package/lib/Utils/mapMap.js +10 -3
- package/lib/Utils/readDataURIToBlob.js +3 -3
- package/lib/Utils/scrollIntoViewWithBlockNearest.d.ts +7 -0
- package/lib/Utils/scrollIntoViewWithBlockNearest.d.ts.map +1 -0
- package/lib/Utils/scrollIntoViewWithBlockNearest.js +48 -0
- package/lib/Utils/shallowEquals.js +7 -3
- package/lib/Utils/singleToArray.js +1 -1
- package/lib/Utils/supportPseudoClass.d.ts +2 -0
- package/lib/Utils/supportPseudoClass.d.ts.map +1 -0
- package/lib/Utils/supportPseudoClass.js +23 -0
- package/lib/Utils/tabbableElements.d.ts +2 -0
- package/lib/Utils/tabbableElements.d.ts.map +1 -0
- package/lib/Utils/tabbableElements.js +2 -2
- package/lib/connectToWebChat.js +17 -7
- package/lib/hooks/index.d.ts +21 -0
- package/lib/hooks/index.d.ts.map +1 -0
- package/lib/hooks/index.js +25 -25
- package/lib/hooks/internal/BypassSpeechSynthesisPonyfill.js +100 -41
- package/lib/hooks/internal/UITracker.js +2 -2
- package/lib/hooks/internal/useChanged.js +8 -1
- package/lib/hooks/internal/useDispatchScrollPosition.js +3 -7
- package/lib/hooks/internal/useDispatchTranscriptFocusByActivityKey.d.ts +2 -0
- package/lib/hooks/internal/useDispatchTranscriptFocusByActivityKey.d.ts.map +1 -0
- package/lib/hooks/internal/useDispatchTranscriptFocusByActivityKey.js +15 -0
- package/lib/hooks/internal/useEnterKeyHint.js +1 -1
- package/lib/hooks/internal/useFocusVisible.d.ts +3 -0
- package/lib/hooks/internal/useFocusVisible.d.ts.map +1 -0
- package/lib/hooks/internal/useFocusVisible.js +48 -0
- package/lib/hooks/internal/useForceRender.js +2 -2
- package/lib/hooks/internal/useForceRenderAtInterval.js +2 -2
- package/lib/hooks/internal/useInternalRenderMarkdownInline.js +1 -1
- package/lib/hooks/internal/useLocalizeAccessKey.js +2 -2
- package/lib/hooks/internal/useMemoWithPrevious.d.ts +3 -0
- package/lib/hooks/internal/useMemoWithPrevious.d.ts.map +1 -0
- package/lib/hooks/internal/useMemoWithPrevious.js +22 -0
- package/lib/hooks/internal/useMemoize.d.ts +14 -0
- package/lib/hooks/internal/useMemoize.d.ts.map +1 -0
- package/lib/hooks/internal/useMemoize.js +12 -3
- package/lib/hooks/internal/useNavigatorPlatform.js +1 -1
- package/lib/hooks/internal/useNonce.js +1 -1
- package/lib/hooks/internal/useObserveFocusVisible.d.ts +3 -0
- package/lib/hooks/internal/useObserveFocusVisible.d.ts.map +1 -0
- package/lib/hooks/internal/useObserveFocusVisible.js +208 -0
- package/lib/hooks/internal/usePrevious.d.ts +2 -0
- package/lib/hooks/internal/usePrevious.d.ts.map +1 -0
- package/lib/hooks/internal/usePrevious.js +18 -0
- package/lib/hooks/internal/useRegisterFocusSendBox.js +1 -1
- package/lib/hooks/internal/useRegisterFocusTranscript.js +1 -1
- package/lib/hooks/internal/useRegisterScrollRelative.js +1 -1
- package/lib/hooks/internal/useRegisterScrollTo.js +1 -1
- package/lib/hooks/internal/useRegisterScrollToEnd.js +1 -1
- package/lib/hooks/internal/useReplaceEmoticon.js +2 -2
- package/lib/hooks/internal/useResumeAudioContext.js +33 -0
- package/lib/hooks/internal/useScrollRelative.js +1 -1
- package/lib/hooks/internal/useSettableDictateAbortable.js +1 -1
- package/lib/hooks/internal/useStateRef.d.ts +3 -0
- package/lib/hooks/internal/useStateRef.d.ts.map +1 -0
- package/lib/hooks/internal/useStateRef.js +40 -0
- package/lib/hooks/internal/useSuggestedActionsAccessKey.js +1 -1
- package/lib/hooks/internal/useUniqueId.d.ts +2 -0
- package/lib/hooks/internal/useUniqueId.d.ts.map +1 -0
- package/lib/hooks/internal/useUniqueId.js +1 -1
- package/lib/hooks/internal/useValueRef.d.ts +3 -0
- package/lib/hooks/internal/useValueRef.d.ts.map +1 -0
- package/lib/hooks/internal/useValueRef.js +25 -0
- package/lib/hooks/useDictateAbortable.d.ts +2 -0
- package/lib/hooks/useDictateAbortable.d.ts.map +1 -0
- package/lib/hooks/useDictateAbortable.js +2 -2
- package/lib/hooks/useFocus.d.ts +2 -0
- package/lib/hooks/useFocus.d.ts.map +1 -0
- package/lib/hooks/useFocus.js +1 -1
- package/lib/hooks/useFocusSendBox.d.ts +3 -0
- package/lib/hooks/useFocusSendBox.d.ts.map +1 -0
- package/lib/hooks/useFocusSendBox.js +2 -1
- package/lib/hooks/useObserveScrollPosition.d.ts +3 -0
- package/lib/hooks/useObserveScrollPosition.d.ts.map +1 -0
- package/lib/hooks/useObserveScrollPosition.js +2 -2
- package/lib/hooks/useObserveTranscriptFocus.d.ts +5 -0
- package/lib/hooks/useObserveTranscriptFocus.d.ts.map +1 -0
- package/lib/hooks/useObserveTranscriptFocus.js +2 -2
- package/lib/hooks/useRenderMarkdownAsHTML.d.ts +5 -0
- package/lib/hooks/useRenderMarkdownAsHTML.d.ts.map +1 -0
- package/lib/hooks/useRenderMarkdownAsHTML.js +2 -2
- package/lib/hooks/useScrollDown.d.ts +4 -0
- package/lib/hooks/useScrollDown.d.ts.map +1 -0
- package/lib/hooks/useScrollDown.js +1 -1
- package/lib/hooks/useScrollTo.d.ts +5 -0
- package/lib/hooks/useScrollTo.d.ts.map +1 -0
- package/lib/hooks/useScrollTo.js +1 -1
- package/lib/hooks/useScrollToEnd.d.ts +2 -0
- package/lib/hooks/useScrollToEnd.d.ts.map +1 -0
- package/lib/hooks/useScrollToEnd.js +1 -1
- package/lib/hooks/useScrollUp.d.ts +4 -0
- package/lib/hooks/useScrollUp.d.ts.map +1 -0
- package/lib/hooks/useScrollUp.js +1 -1
- package/lib/hooks/useSendFiles.d.ts +2 -0
- package/lib/hooks/useSendFiles.d.ts.map +1 -0
- package/lib/hooks/useSendFiles.js +3 -3
- package/lib/hooks/useStyleSet.d.ts +2 -0
- package/lib/hooks/useStyleSet.d.ts.map +1 -0
- package/lib/hooks/useStyleSet.js +1 -1
- package/lib/hooks/useWebSpeechPonyfill.d.ts +3 -0
- package/lib/hooks/useWebSpeechPonyfill.d.ts.map +1 -0
- package/lib/hooks/useWebSpeechPonyfill.js +1 -1
- package/lib/index.d.ts +219 -22
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +22 -20
- package/lib/providers/ActivityTree/ActivityTreeComposer.d.ts +5 -0
- package/lib/providers/ActivityTree/ActivityTreeComposer.d.ts.map +1 -0
- package/lib/providers/ActivityTree/ActivityTreeComposer.js +86 -0
- package/lib/providers/ActivityTree/private/Context.d.ts +9 -0
- package/lib/providers/ActivityTree/private/Context.d.ts.map +1 -0
- package/lib/providers/ActivityTree/private/Context.js +13 -0
- package/lib/providers/ActivityTree/private/types.d.ts +10 -0
- package/lib/providers/ActivityTree/private/types.d.ts.map +1 -0
- package/lib/providers/ActivityTree/private/types.js +2 -0
- package/lib/providers/ActivityTree/private/useActivitiesWithRenderer.d.ts +4 -0
- package/lib/providers/ActivityTree/private/useActivitiesWithRenderer.d.ts.map +1 -0
- package/lib/providers/ActivityTree/private/useActivitiesWithRenderer.js +58 -0
- package/lib/providers/ActivityTree/private/useActivityTreeWithRenderer.d.ts +5 -0
- package/lib/providers/ActivityTree/private/useActivityTreeWithRenderer.d.ts.map +1 -0
- package/lib/providers/ActivityTree/private/useActivityTreeWithRenderer.js +166 -0
- package/lib/providers/ActivityTree/private/useContext.d.ts +3 -0
- package/lib/providers/ActivityTree/private/useContext.d.ts.map +1 -0
- package/lib/providers/ActivityTree/private/useContext.js +24 -0
- package/lib/providers/ActivityTree/useActivityTreeWithRenderer.d.ts +8 -0
- package/lib/providers/ActivityTree/useActivityTreeWithRenderer.d.ts.map +1 -0
- package/lib/providers/ActivityTree/useActivityTreeWithRenderer.js +17 -0
- package/lib/providers/LiveRegionTwin/LiveRegionTwinComposer.d.ts +35 -0
- package/lib/providers/LiveRegionTwin/LiveRegionTwinComposer.d.ts.map +1 -0
- package/lib/providers/LiveRegionTwin/LiveRegionTwinComposer.js +156 -0
- package/lib/providers/LiveRegionTwin/private/Context.d.ts +10 -0
- package/lib/providers/LiveRegionTwin/private/Context.d.ts.map +1 -0
- package/lib/providers/LiveRegionTwin/private/Context.js +13 -0
- package/lib/providers/LiveRegionTwin/private/LiveRegionTwinContainer.d.ts +11 -0
- package/lib/providers/LiveRegionTwin/private/LiveRegionTwinContainer.d.ts.map +1 -0
- package/lib/providers/LiveRegionTwin/private/LiveRegionTwinContainer.js +86 -0
- package/lib/providers/LiveRegionTwin/private/types.d.ts +8 -0
- package/lib/providers/LiveRegionTwin/private/types.d.ts.map +1 -0
- package/lib/providers/LiveRegionTwin/private/types.js +2 -0
- package/lib/providers/LiveRegionTwin/private/useContext.d.ts +3 -0
- package/lib/providers/LiveRegionTwin/private/useContext.d.ts.map +1 -0
- package/lib/providers/LiveRegionTwin/private/useContext.js +24 -0
- package/lib/providers/LiveRegionTwin/private/useMarkAllAsRenderedEffect.d.ts +2 -0
- package/lib/providers/LiveRegionTwin/private/useMarkAllAsRenderedEffect.d.ts.map +1 -0
- package/lib/providers/LiveRegionTwin/private/useMarkAllAsRenderedEffect.js +24 -0
- package/lib/providers/LiveRegionTwin/private/useStaticElementEntries.d.ts +3 -0
- package/lib/providers/LiveRegionTwin/private/useStaticElementEntries.d.ts.map +1 -0
- package/lib/providers/LiveRegionTwin/private/useStaticElementEntries.js +15 -0
- package/lib/providers/LiveRegionTwin/useQueueStaticElement.d.ts +8 -0
- package/lib/providers/LiveRegionTwin/useQueueStaticElement.d.ts.map +1 -0
- package/lib/providers/LiveRegionTwin/useQueueStaticElement.js +20 -0
- package/lib/providers/TranscriptFocus/TranscriptFocusComposer.d.ts +7 -0
- package/lib/providers/TranscriptFocus/TranscriptFocusComposer.d.ts.map +1 -0
- package/lib/providers/TranscriptFocus/TranscriptFocusComposer.js +184 -0
- package/lib/providers/TranscriptFocus/private/Context.d.ts +12 -0
- package/lib/providers/TranscriptFocus/private/Context.d.ts.map +1 -0
- package/lib/providers/TranscriptFocus/private/Context.js +13 -0
- package/lib/providers/TranscriptFocus/private/useContext.d.ts +2 -0
- package/lib/providers/TranscriptFocus/private/useContext.d.ts.map +1 -0
- package/lib/providers/TranscriptFocus/private/useContext.js +24 -0
- package/lib/providers/TranscriptFocus/useActiveDescendantId.d.ts +2 -0
- package/lib/providers/TranscriptFocus/useActiveDescendantId.d.ts.map +1 -0
- package/lib/providers/TranscriptFocus/useActiveDescendantId.js +15 -0
- package/lib/providers/TranscriptFocus/useFocusByActivityKey.d.ts +8 -0
- package/lib/providers/TranscriptFocus/useFocusByActivityKey.d.ts.map +1 -0
- package/lib/providers/TranscriptFocus/useFocusByActivityKey.js +21 -0
- package/lib/providers/TranscriptFocus/useFocusRelativeActivity.d.ts +2 -0
- package/lib/providers/TranscriptFocus/useFocusRelativeActivity.d.ts.map +1 -0
- package/lib/providers/TranscriptFocus/useFocusRelativeActivity.js +15 -0
- package/lib/providers/TranscriptFocus/useFocusedActivityKey.d.ts +2 -0
- package/lib/providers/TranscriptFocus/useFocusedActivityKey.d.ts.map +1 -0
- package/lib/providers/TranscriptFocus/useFocusedActivityKey.js +15 -0
- package/lib/providers/TranscriptFocus/useFocusedExplicitly.d.ts +2 -0
- package/lib/providers/TranscriptFocus/useFocusedExplicitly.d.ts.map +1 -0
- package/lib/providers/TranscriptFocus/useFocusedExplicitly.js +15 -0
- package/lib/providers/TranscriptFocus/useGetDescendantIdByActivityKey.d.ts +2 -0
- package/lib/providers/TranscriptFocus/useGetDescendantIdByActivityKey.d.ts.map +1 -0
- package/lib/providers/TranscriptFocus/useGetDescendantIdByActivityKey.js +15 -0
- package/lib/tsconfig.json +1 -1
- package/lib/types/ScrollPosition.d.ts +6 -0
- package/lib/types/ScrollPosition.d.ts.map +1 -0
- package/lib/types/ScrollPosition.js +2 -0
- package/package.json +33 -40
- package/src/Activity/{Avatar.js → Avatar.tsx} +9 -2
- package/src/Activity/{Bubble.js → Bubble.tsx} +10 -2
- package/src/Activity/CarouselFilmStrip.js +20 -29
- package/src/Activity/CarouselFilmStripAttachment.js +91 -0
- package/src/Activity/{Speak.js → Speak.tsx} +12 -5
- package/src/Activity/{StackedLayout.js → StackedLayout.tsx} +46 -20
- package/src/Attachment/Assets/DownloadIcon.js +8 -1
- package/src/Attachment/{AudioContent.js → AudioContent.tsx} +12 -3
- package/src/Attachment/FileAttachment.js +1 -1
- package/src/Attachment/{FileContent.js → FileContent.tsx} +22 -7
- package/src/Attachment/{HTMLVideoContent.js → HTMLVideoContent.tsx} +10 -2
- package/src/Attachment/{ImageContent.js → ImageContent.tsx} +7 -2
- package/src/Attachment/{TextContent.js → TextContent.tsx} +14 -7
- package/src/Attachment/{VideoContent.js → VideoContent.tsx} +10 -2
- package/src/Attachment/{VimeoContent.js → VimeoContent.tsx} +15 -8
- package/src/Attachment/{YouTubeContent.js → YouTubeContent.tsx} +12 -5
- package/src/{BasicSendBox.js → BasicSendBox.tsx} +9 -5
- package/src/BasicToaster.js +9 -5
- package/src/BasicTranscript.tsx +878 -0
- package/src/{BasicTypingIndicator.js → BasicTypingIndicator.tsx} +4 -3
- package/src/{BasicWebChat.js → BasicWebChat.tsx} +13 -4
- package/src/{Composer.js → Composer.tsx} +82 -71
- package/src/Dictation.js +13 -4
- package/src/{ErrorBox.js → ErrorBox.tsx} +7 -2
- package/src/Middleware/Activity/createCoreMiddleware.tsx +78 -0
- package/src/Middleware/ActivityStatus/SendStatus/{SendStatus.js → SendStatus.tsx} +8 -3
- package/src/Middleware/ActivityStatus/{Timestamp.js → Timestamp.tsx} +8 -2
- package/src/Middleware/ActivityStatus/{createCoreMiddleware.js → createCoreMiddleware.tsx} +3 -1
- package/src/Middleware/ActivityStatus/createSendStatusMiddleware.js +10 -6
- package/src/Middleware/ActivityStatus/createTimestampMiddleware.js +10 -8
- package/src/Middleware/Attachment/createCoreMiddleware.tsx +44 -0
- package/src/Middleware/AttachmentForScreenReader/createCoreMiddleware.tsx +40 -0
- package/src/Middleware/Avatar/{createCoreMiddleware.js → createCoreMiddleware.tsx} +19 -11
- package/src/Middleware/CardAction/createCoreMiddleware.js +51 -42
- package/src/Middleware/GroupActivities/createCoreMiddleware.js +6 -4
- package/src/Middleware/ScrollToEndButton/ScrollToEndButton.js +45 -0
- package/src/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.ts +19 -0
- package/src/Middleware/Toast/createCoreMiddleware.tsx +24 -0
- package/src/Middleware/TypingIndicator/{createCoreMiddleware.js → createCoreMiddleware.tsx} +10 -5
- package/src/{ReactWebChat.js → ReactWebChat.tsx} +25 -7
- package/src/ScreenReaderActivity.js +65 -40
- package/src/{ScreenReaderText.js → ScreenReaderText.tsx} +27 -9
- package/src/SendBox/Assets/AttachmentIcon.js +1 -1
- package/src/SendBox/Assets/MicrophoneIcon.js +1 -1
- package/src/SendBox/{AutoResizeTextArea.js → AutoResizeTextArea.tsx} +30 -4
- package/src/SendBox/{DictationInterims.js → DictationInterims.tsx} +6 -2
- package/src/SendBox/{IconButton.js → IconButton.tsx} +19 -3
- package/src/SendBox/{MicrophoneButton.js → MicrophoneButton.tsx} +35 -25
- package/src/SendBox/{SendButton.js → SendButton.tsx} +8 -3
- package/src/SendBox/{SuggestedAction.js → SuggestedAction.tsx} +62 -39
- package/src/SendBox/{SuggestedActions.js → SuggestedActions.tsx} +70 -22
- package/src/SendBox/{TextBox.js → TextBox.tsx} +21 -7
- package/src/SendBox/{UploadButton.js → UploadButton.tsx} +7 -3
- package/src/Styles/StyleSet/BasicTranscript.ts +34 -20
- package/src/Styles/StyleSet/Bubble.ts +0 -1
- package/src/Styles/StyleSet/CarouselFilmStrip.ts +12 -30
- package/src/Styles/StyleSet/CarouselFilmStripAttachment.ts +57 -0
- package/src/Styles/StyleSet/KeyboardHelp.ts +157 -0
- package/src/Styles/StyleSet/ScrollToEndButton.ts +33 -31
- package/src/Styles/StyleSet/SendBoxButton.ts +84 -13
- package/src/Styles/StyleSet/SendBoxTextBox.ts +1 -2
- package/src/Styles/StyleSet/StackedLayout.ts +13 -11
- package/src/Styles/StyleSet/SuggestedAction.ts +130 -43
- package/src/Styles/StyleSet/SuggestedActions.ts +13 -5
- package/src/Styles/createStyleSet.ts +43 -40
- package/src/Styles/mirrorStyle.js +10 -2
- package/src/Toast/CollapseIcon.js +9 -1
- package/src/Toast/DismissIcon.js +9 -1
- package/src/Toast/ExpandIcon.js +9 -1
- package/src/Toast/NotificationIcon.js +4 -1
- package/src/Toast/createToastMiddleware.tsx +15 -0
- package/src/Transcript/ActivityRow.tsx +124 -0
- package/src/Transcript/ActivityTextAlt.tsx +32 -0
- package/src/Transcript/FocusTrap.tsx +64 -0
- package/src/Transcript/KeyboardHelp.tsx +282 -0
- package/src/Transcript/LiveRegionTranscript.tsx +181 -0
- package/src/Transcript/types.ts +1 -0
- package/src/Transcript/useActivityAccessibleName.ts +85 -0
- package/src/Utils/{AccessibleButton.js → AccessibleButton.tsx} +19 -4
- package/src/Utils/{AccessibleInputText.js → AccessibleInputText.tsx} +48 -5
- package/src/Utils/{AccessibleTextArea.js → AccessibleTextArea.tsx} +67 -6
- package/src/Utils/{FocusRedirector.js → FocusRedirector.tsx} +21 -8
- package/src/Utils/InlineMarkdown.js +18 -2
- package/src/Utils/TypeFocusSink/FocusBox.js +4 -4
- package/src/Utils/TypeFocusSink/getTabIndex.ts +1 -1
- package/src/Utils/TypeFocusSink/inputtableKey.ts +5 -1
- package/src/Utils/activityAltText.ts +135 -0
- package/src/Utils/createCustomEvent.js +7 -1
- package/src/Utils/detectBrowser.js +2 -1
- package/src/Utils/findAncestor.ts +12 -0
- package/src/Utils/getActivityUniqueId.ts +5 -0
- package/src/Utils/intersectionOf.spec.js +2 -0
- package/src/Utils/intersectionOf.ts +14 -0
- package/src/Utils/isZeroOrPositive.ts +7 -0
- package/src/Utils/mapMap.js +7 -1
- package/src/Utils/mapMap.spec.js +2 -0
- package/src/Utils/removeInline.spec.js +2 -0
- package/src/Utils/scrollIntoViewWithBlockNearest.ts +20 -0
- package/src/Utils/shallowEquals.js +8 -1
- package/src/Utils/{singleToArray.js → singleToArray.ts} +1 -1
- package/src/Utils/supportPseudoClass.ts +17 -0
- package/src/Utils/{tabbableElements.js → tabbableElements.ts} +6 -5
- package/src/Utils/walkMarkdownTokens.spec.js +3 -3
- package/src/connectToWebChat.js +11 -4
- package/src/hooks/{index.js → index.ts} +1 -0
- package/src/hooks/internal/BypassSpeechSynthesisPonyfill.js +70 -17
- package/src/hooks/internal/useChanged.ts +17 -0
- package/src/hooks/internal/useDispatchScrollPosition.js +1 -3
- package/src/hooks/internal/useDispatchTranscriptFocusByActivityKey.ts +5 -0
- package/src/hooks/internal/useFocusVisible.ts +22 -0
- package/src/hooks/internal/useMemoWithPrevious.ts +16 -0
- package/src/hooks/internal/useMemoize.spec.js +3 -1
- package/src/hooks/internal/useMemoize.ts +53 -0
- package/src/hooks/internal/useObserveFocusVisible.ts +252 -0
- package/src/hooks/internal/usePrevious.ts +12 -0
- package/src/hooks/internal/useResumeAudioContext.ts +7 -0
- package/src/hooks/internal/useScrollRelative.js +4 -3
- package/src/hooks/internal/useStateRef.ts +31 -0
- package/src/hooks/internal/{useUniqueId.js → useUniqueId.ts} +1 -1
- package/src/hooks/internal/useValueRef.ts +22 -0
- package/src/hooks/{useDictateAbortable.js → useDictateAbortable.ts} +1 -1
- package/src/hooks/{useFocus.js → useFocus.ts} +1 -1
- package/src/hooks/{useFocusSendBox.js → useFocusSendBox.ts} +2 -1
- package/src/hooks/{useObserveScrollPosition.js → useObserveScrollPosition.ts} +6 -1
- package/src/hooks/{useObserveTranscriptFocus.js → useObserveTranscriptFocus.ts} +6 -1
- package/src/hooks/useRenderMarkdownAsHTML.ts +23 -0
- package/src/hooks/{useScrollDown.js → useScrollDown.ts} +1 -1
- package/src/hooks/useScrollTo.ts +16 -0
- package/src/hooks/useScrollToEnd.ts +12 -0
- package/src/hooks/{useScrollUp.js → useScrollUp.ts} +1 -1
- package/src/hooks/{useSendFiles.js → useSendFiles.ts} +3 -2
- package/src/hooks/{useStyleSet.js → useStyleSet.ts} +1 -1
- package/src/hooks/useWebSpeechPonyfill.ts +7 -0
- package/src/index.ts +7 -12
- package/src/providers/ActivityTree/ActivityTreeComposer.tsx +74 -0
- package/src/providers/ActivityTree/private/Context.ts +12 -0
- package/src/providers/ActivityTree/private/types.ts +12 -0
- package/src/providers/ActivityTree/private/useActivitiesWithRenderer.ts +66 -0
- package/src/providers/ActivityTree/private/useActivityTreeWithRenderer.ts +140 -0
- package/src/providers/ActivityTree/private/useContext.ts +15 -0
- package/src/providers/ActivityTree/useActivityTreeWithRenderer.ts +16 -0
- package/src/providers/LiveRegionTwin/LiveRegionTwinComposer.tsx +154 -0
- package/src/providers/LiveRegionTwin/private/Context.ts +15 -0
- package/src/providers/LiveRegionTwin/private/LiveRegionTwinContainer.tsx +64 -0
- package/src/providers/LiveRegionTwin/private/types.ts +10 -0
- package/src/providers/LiveRegionTwin/private/useContext.ts +15 -0
- package/src/providers/LiveRegionTwin/private/useMarkAllAsRenderedEffect.ts +13 -0
- package/src/providers/LiveRegionTwin/private/useStaticElementEntries.ts +7 -0
- package/src/providers/LiveRegionTwin/useQueueStaticElement.ts +12 -0
- package/src/providers/TranscriptFocus/TranscriptFocusComposer.tsx +180 -0
- package/src/providers/TranscriptFocus/private/Context.ts +16 -0
- package/src/providers/TranscriptFocus/private/useContext.ts +13 -0
- package/src/providers/TranscriptFocus/useActiveDescendantId.ts +5 -0
- package/src/providers/TranscriptFocus/useFocusByActivityKey.ts +14 -0
- package/src/providers/TranscriptFocus/useFocusRelativeActivity.ts +5 -0
- package/src/providers/TranscriptFocus/useFocusedActivityKey.ts +5 -0
- package/src/providers/TranscriptFocus/useFocusedExplicitly.ts +5 -0
- package/src/providers/TranscriptFocus/useGetDescendantIdByActivityKey.ts +5 -0
- package/src/tsconfig.json +1 -1
- package/src/types/ScrollPosition.ts +6 -0
- package/.eslintignore +0 -9
- package/lib/Activity/ScrollToEndButton.js +0 -81
- package/lib/Attachment/Assets/ErrorIcon.js +0 -22
- package/lib/Utils/findLastIndex.js +0 -32
- package/lib/Utils/remarkStripMarkdown.js +0 -26
- package/lib/hooks/internal/useAcknowledgedActivity.js +0 -90
- package/lib/hooks/internal/useDispatchTranscriptFocus.js +0 -19
- package/lib/hooks/internal/useStripMarkdown.js +0 -19
- package/src/Activity/ScrollToEndButton.js +0 -58
- package/src/Attachment/Assets/ErrorIcon.js +0 -9
- package/src/BasicTranscript.js +0 -1123
- package/src/Middleware/Activity/createCoreMiddleware.js +0 -71
- package/src/Middleware/Attachment/createCoreMiddleware.js +0 -43
- package/src/Middleware/AttachmentForScreenReader/createCoreMiddleware.js +0 -37
- package/src/Middleware/Toast/createCoreMiddleware.js +0 -20
- package/src/Toast/createToastMiddleware.js +0 -11
- package/src/Utils/findLastIndex.js +0 -11
- package/src/Utils/findLastIndex.spec.js +0 -29
- package/src/Utils/getActivityUniqueId.js +0 -3
- package/src/Utils/intersectionOf.js +0 -11
- package/src/Utils/isZeroOrPositive.js +0 -4
- package/src/Utils/remarkStripMarkdown.js +0 -13
- package/src/hooks/internal/useAcknowledgedActivity.js +0 -65
- package/src/hooks/internal/useChanged.js +0 -10
- package/src/hooks/internal/useDispatchTranscriptFocus.js +0 -7
- package/src/hooks/internal/useMemoize.js +0 -37
- package/src/hooks/internal/useStripMarkdown.js +0 -7
- package/src/hooks/useRenderMarkdownAsHTML.js +0 -20
- package/src/hooks/useScrollTo.js +0 -11
- package/src/hooks/useScrollToEnd.js +0 -11
- package/src/hooks/useWebSpeechPonyfill.js +0 -5
- package/src/index.tsx +0 -35
|
@@ -0,0 +1,878 @@
|
|
|
1
|
+
import { hooks } from 'botframework-webchat-api';
|
|
2
|
+
import {
|
|
3
|
+
Composer as ReactScrollToBottomComposer,
|
|
4
|
+
Panel as ReactScrollToBottomPanel,
|
|
5
|
+
useAnimatingToEnd,
|
|
6
|
+
useAtEnd,
|
|
7
|
+
useObserveScrollPosition,
|
|
8
|
+
useScrollTo,
|
|
9
|
+
useScrollToEnd,
|
|
10
|
+
useSticky
|
|
11
|
+
} from 'react-scroll-to-bottom';
|
|
12
|
+
import classNames from 'classnames';
|
|
13
|
+
import PropTypes from 'prop-types';
|
|
14
|
+
import React, { forwardRef, Fragment, useCallback, useMemo, useRef } from 'react';
|
|
15
|
+
|
|
16
|
+
import type { ActivityComponentFactory, AvatarComponentFactory } from 'botframework-webchat-api';
|
|
17
|
+
import type { ActivityElementMap } from './Transcript/types';
|
|
18
|
+
import type { DirectLineActivity } from 'botframework-webchat-core';
|
|
19
|
+
import type { FC, KeyboardEventHandler, MutableRefObject, ReactNode, VFC } from 'react';
|
|
20
|
+
|
|
21
|
+
import { android } from './Utils/detectBrowser';
|
|
22
|
+
import ActivityRow from './Transcript/ActivityRow';
|
|
23
|
+
import BasicTypingIndicator from './BasicTypingIndicator';
|
|
24
|
+
import FocusRedirector from './Utils/FocusRedirector';
|
|
25
|
+
import inputtableKey from './Utils/TypeFocusSink/inputtableKey';
|
|
26
|
+
import isZeroOrPositive from './Utils/isZeroOrPositive';
|
|
27
|
+
import KeyboardHelp from './Transcript/KeyboardHelp';
|
|
28
|
+
import LiveRegionTranscript from './Transcript/LiveRegionTranscript';
|
|
29
|
+
// TODO: [P2] #4133 Rename to "getTabbableElements".
|
|
30
|
+
import tabbableElements from './Utils/tabbableElements';
|
|
31
|
+
import TranscriptFocusComposer from './providers/TranscriptFocus/TranscriptFocusComposer';
|
|
32
|
+
import useActiveDescendantId from './providers/TranscriptFocus/useActiveDescendantId';
|
|
33
|
+
import useActivityTreeWithRenderer from './providers/ActivityTree/useActivityTreeWithRenderer';
|
|
34
|
+
import useDispatchScrollPosition from './hooks/internal/useDispatchScrollPosition';
|
|
35
|
+
import useDispatchTranscriptFocusByActivityKey from './hooks/internal/useDispatchTranscriptFocusByActivityKey';
|
|
36
|
+
import useFocus from './hooks/useFocus';
|
|
37
|
+
import useFocusByActivityKey from './providers/TranscriptFocus/useFocusByActivityKey';
|
|
38
|
+
import useFocusedActivityKey from './providers/TranscriptFocus/useFocusedActivityKey';
|
|
39
|
+
import useFocusedExplicitly from './providers/TranscriptFocus/useFocusedExplicitly';
|
|
40
|
+
import useFocusRelativeActivity from './providers/TranscriptFocus/useFocusRelativeActivity';
|
|
41
|
+
import useObserveFocusVisible from './hooks/internal/useObserveFocusVisible';
|
|
42
|
+
import usePrevious from './hooks/internal/usePrevious';
|
|
43
|
+
import useRegisterFocusTranscript from './hooks/internal/useRegisterFocusTranscript';
|
|
44
|
+
import useRegisterScrollRelative from './hooks/internal/useRegisterScrollRelative';
|
|
45
|
+
import useRegisterScrollTo from './hooks/internal/useRegisterScrollTo';
|
|
46
|
+
import useRegisterScrollToEnd from './hooks/internal/useRegisterScrollToEnd';
|
|
47
|
+
import useStyleSet from './hooks/useStyleSet';
|
|
48
|
+
import useStyleToEmotionObject from './hooks/internal/useStyleToEmotionObject';
|
|
49
|
+
import useUniqueId from './hooks/internal/useUniqueId';
|
|
50
|
+
import useValueRef from './hooks/internal/useValueRef';
|
|
51
|
+
|
|
52
|
+
const {
|
|
53
|
+
useActivityKeys,
|
|
54
|
+
useActivityKeysByRead,
|
|
55
|
+
useCreateActivityStatusRenderer,
|
|
56
|
+
useCreateAvatarRenderer,
|
|
57
|
+
useCreateScrollToEndButtonRenderer,
|
|
58
|
+
useDirection,
|
|
59
|
+
useGetActivityByKey,
|
|
60
|
+
useGetKeyByActivity,
|
|
61
|
+
useGetKeyByActivityId,
|
|
62
|
+
useLastAcknowledgedActivityKey,
|
|
63
|
+
useLocalizer,
|
|
64
|
+
useMarkActivityKeyAsRead,
|
|
65
|
+
useMarkAllAsAcknowledged,
|
|
66
|
+
useStyleOptions
|
|
67
|
+
} = hooks;
|
|
68
|
+
|
|
69
|
+
const ROOT_STYLE = {
|
|
70
|
+
'&.webchat__basic-transcript': {
|
|
71
|
+
display: 'flex',
|
|
72
|
+
flexDirection: 'column',
|
|
73
|
+
overflow: 'hidden',
|
|
74
|
+
// Make sure to set "position: relative" here to form another stacking context for the scroll-to-end button.
|
|
75
|
+
// Stacking context help isolating elements that use "z-index" from global pollution.
|
|
76
|
+
// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
|
|
77
|
+
position: 'relative',
|
|
78
|
+
|
|
79
|
+
'& .webchat__basic-transcript__filler': {
|
|
80
|
+
flex: 1
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
'& .webchat__basic-transcript__scrollable': {
|
|
84
|
+
display: 'flex',
|
|
85
|
+
flexDirection: 'column',
|
|
86
|
+
overflowX: 'hidden',
|
|
87
|
+
WebkitOverflowScrolling: 'touch'
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
'& .webchat__basic-transcript__transcript': {
|
|
91
|
+
listStyleType: 'none'
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
type RenderingElement = {
|
|
97
|
+
activity: DirectLineActivity & {
|
|
98
|
+
channelData?: {
|
|
99
|
+
['webchat:fallback-text']: string;
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
callbackRef: (element: HTMLElement) => void;
|
|
103
|
+
hideTimestamp: boolean;
|
|
104
|
+
key: string;
|
|
105
|
+
renderActivity: Exclude<ReturnType<ActivityComponentFactory>, false>;
|
|
106
|
+
renderActivityStatus: (props: { hideTimestamp?: boolean }) => ReactNode;
|
|
107
|
+
renderAvatar: AvatarComponentFactory;
|
|
108
|
+
showCallout: boolean;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
type ScrollBehavior = 'auto' | 'smooth';
|
|
112
|
+
type ScrollToOptions = { behavior?: ScrollBehavior };
|
|
113
|
+
type ScrollToPosition = { activityID?: string; scrollTop?: number };
|
|
114
|
+
|
|
115
|
+
type InternalTranscriptProps = {
|
|
116
|
+
activityElementMapRef: MutableRefObject<ActivityElementMap>;
|
|
117
|
+
className?: string;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// TODO: [P1] #4133 Add telemetry for computing how many re-render done so far.
|
|
121
|
+
const InternalTranscript = forwardRef<HTMLDivElement, InternalTranscriptProps>(
|
|
122
|
+
({ activityElementMapRef, className }, ref) => {
|
|
123
|
+
const [{ basicTranscript: basicTranscriptStyleSet }] = useStyleSet();
|
|
124
|
+
const [{ bubbleFromUserNubOffset, bubbleNubOffset, groupTimestamp, showAvatarInGroup }] = useStyleOptions();
|
|
125
|
+
const [activeDescendantId] = useActiveDescendantId();
|
|
126
|
+
const [activityWithRendererTree] = useActivityTreeWithRenderer();
|
|
127
|
+
const [direction] = useDirection();
|
|
128
|
+
const [focusedActivityKey] = useFocusedActivityKey();
|
|
129
|
+
const [focusedExplicitly] = useFocusedExplicitly();
|
|
130
|
+
const createActivityStatusRenderer = useCreateActivityStatusRenderer();
|
|
131
|
+
const createAvatarRenderer = useCreateAvatarRenderer();
|
|
132
|
+
const focus = useFocus();
|
|
133
|
+
const focusByActivityKey = useFocusByActivityKey();
|
|
134
|
+
const focusRelativeActivity = useFocusRelativeActivity();
|
|
135
|
+
const getActivityByKey = useGetActivityByKey();
|
|
136
|
+
const getKeyByActivity = useGetKeyByActivity();
|
|
137
|
+
const getKeyByActivityId = useGetKeyByActivityId();
|
|
138
|
+
const localize = useLocalizer();
|
|
139
|
+
const rootClassName = useStyleToEmotionObject()(ROOT_STYLE) + '';
|
|
140
|
+
const rootElementRef = useRef<HTMLDivElement>();
|
|
141
|
+
const terminatorLabelId = useUniqueId('webchat__basic-transcript__terminator-label');
|
|
142
|
+
const terminatorRef = useRef<HTMLDivElement>();
|
|
143
|
+
|
|
144
|
+
const focusedActivityKeyRef = useValueRef(focusedActivityKey);
|
|
145
|
+
const hideAllTimestamps = groupTimestamp === false;
|
|
146
|
+
const terminatorText = localize('TRANSCRIPT_TERMINATOR_TEXT');
|
|
147
|
+
const transcriptAriaLabel = localize('TRANSCRIPT_ARIA_LABEL_ALT');
|
|
148
|
+
|
|
149
|
+
const callbackRef = useCallback(
|
|
150
|
+
(element: HTMLDivElement) => {
|
|
151
|
+
if (typeof ref === 'function') {
|
|
152
|
+
ref(element);
|
|
153
|
+
} else {
|
|
154
|
+
ref.current = element;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
rootElementRef.current = element;
|
|
158
|
+
},
|
|
159
|
+
[ref, rootElementRef]
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Flatten the tree back into an array with information related to rendering.
|
|
163
|
+
const renderingElements = useMemo(() => {
|
|
164
|
+
const renderingElements: RenderingElement[] = [];
|
|
165
|
+
const topSideBotNub = isZeroOrPositive(bubbleNubOffset);
|
|
166
|
+
const topSideUserNub = isZeroOrPositive(bubbleFromUserNubOffset);
|
|
167
|
+
|
|
168
|
+
activityWithRendererTree.forEach(entriesWithSameSender => {
|
|
169
|
+
const [[{ activity: firstActivity }]] = entriesWithSameSender;
|
|
170
|
+
const renderAvatar = createAvatarRenderer({ activity: firstActivity });
|
|
171
|
+
|
|
172
|
+
entriesWithSameSender.forEach((entriesWithSameSenderAndStatus, indexWithinSenderGroup) => {
|
|
173
|
+
const firstInSenderGroup = !indexWithinSenderGroup;
|
|
174
|
+
const lastInSenderGroup = indexWithinSenderGroup === entriesWithSameSender.length - 1;
|
|
175
|
+
|
|
176
|
+
entriesWithSameSenderAndStatus.forEach(({ activity, renderActivity }, indexWithinSenderAndStatusGroup) => {
|
|
177
|
+
// We only show the timestamp at the end of the sender group. But we always show the "Send failed, retry" prompt.
|
|
178
|
+
const firstInSenderAndStatusGroup = !indexWithinSenderAndStatusGroup;
|
|
179
|
+
const key: string = getKeyByActivity(activity);
|
|
180
|
+
const lastInSenderAndStatusGroup =
|
|
181
|
+
indexWithinSenderAndStatusGroup === entriesWithSameSenderAndStatus.length - 1;
|
|
182
|
+
const renderActivityStatus = createActivityStatusRenderer({
|
|
183
|
+
activity,
|
|
184
|
+
nextVisibleActivity: undefined
|
|
185
|
+
});
|
|
186
|
+
const topSideNub = activity.from?.role === 'user' ? topSideUserNub : topSideBotNub;
|
|
187
|
+
|
|
188
|
+
let showCallout: boolean;
|
|
189
|
+
|
|
190
|
+
// Depending on the "showAvatarInGroup" setting, the avatar will render in different positions.
|
|
191
|
+
if (showAvatarInGroup === 'sender') {
|
|
192
|
+
if (topSideNub) {
|
|
193
|
+
showCallout = firstInSenderGroup && firstInSenderAndStatusGroup;
|
|
194
|
+
} else {
|
|
195
|
+
showCallout = lastInSenderGroup && lastInSenderAndStatusGroup;
|
|
196
|
+
}
|
|
197
|
+
} else if (showAvatarInGroup === 'status') {
|
|
198
|
+
if (topSideNub) {
|
|
199
|
+
showCallout = firstInSenderAndStatusGroup;
|
|
200
|
+
} else {
|
|
201
|
+
showCallout = lastInSenderAndStatusGroup;
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
showCallout = true;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
renderingElements.push({
|
|
208
|
+
activity,
|
|
209
|
+
|
|
210
|
+
// After the element is mounted, set it to activityElementsRef.
|
|
211
|
+
callbackRef: activityElement => {
|
|
212
|
+
activityElement
|
|
213
|
+
? activityElementMapRef.current.set(key, activityElement)
|
|
214
|
+
: activityElementMapRef.current.delete(key);
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
// "hideTimestamp" is a render-time parameter for renderActivityStatus().
|
|
218
|
+
// If true, it will hide the timestamp, but it will continue to show the
|
|
219
|
+
// retry prompt. And show the screen reader version of the timestamp.
|
|
220
|
+
hideTimestamp:
|
|
221
|
+
hideAllTimestamps || indexWithinSenderAndStatusGroup !== entriesWithSameSenderAndStatus.length - 1,
|
|
222
|
+
key,
|
|
223
|
+
renderActivity,
|
|
224
|
+
renderActivityStatus,
|
|
225
|
+
renderAvatar,
|
|
226
|
+
showCallout
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return renderingElements;
|
|
233
|
+
}, [
|
|
234
|
+
activityElementMapRef,
|
|
235
|
+
activityWithRendererTree,
|
|
236
|
+
bubbleFromUserNubOffset,
|
|
237
|
+
bubbleNubOffset,
|
|
238
|
+
createActivityStatusRenderer,
|
|
239
|
+
createAvatarRenderer,
|
|
240
|
+
getKeyByActivity,
|
|
241
|
+
hideAllTimestamps,
|
|
242
|
+
showAvatarInGroup
|
|
243
|
+
]);
|
|
244
|
+
|
|
245
|
+
const scrollToBottomScrollTo: (scrollTop: number, options?: ScrollToOptions) => void = useScrollTo();
|
|
246
|
+
const scrollToBottomScrollToEnd: (options?: ScrollToOptions) => void = useScrollToEnd();
|
|
247
|
+
|
|
248
|
+
const scrollTo = useCallback(
|
|
249
|
+
(position: ScrollToPosition, { behavior = 'auto' }: ScrollToOptions = {}) => {
|
|
250
|
+
if (!position) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
'botframework-webchat: First argument passed to "useScrollTo" must be a ScrollPosition object.'
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const { activityID: activityId, scrollTop } = position;
|
|
257
|
+
|
|
258
|
+
if (typeof scrollTop !== 'undefined') {
|
|
259
|
+
scrollToBottomScrollTo(scrollTop, { behavior });
|
|
260
|
+
} else if (typeof activityId !== 'undefined') {
|
|
261
|
+
const activityBoundingBoxElement = activityElementMapRef.current
|
|
262
|
+
.get(getKeyByActivityId(activityId))
|
|
263
|
+
?.querySelector('.webchat__basic-transcript__activity-active-descendant');
|
|
264
|
+
|
|
265
|
+
const scrollableElement = rootElementRef.current.querySelector('.webchat__basic-transcript__scrollable');
|
|
266
|
+
|
|
267
|
+
if (scrollableElement && activityBoundingBoxElement) {
|
|
268
|
+
// ESLint conflict with TypeScript. The result of getClientRects() is not an Array but DOMRectList, and cannot be destructured.
|
|
269
|
+
// eslint-disable-next-line prefer-destructuring
|
|
270
|
+
const { height: activityHeight, y: activityY } = activityBoundingBoxElement.getClientRects()[0];
|
|
271
|
+
|
|
272
|
+
// ESLint conflict with TypeScript. The result of getClientRects() is not an Array but DOMRectList, and cannot be destructured.
|
|
273
|
+
// eslint-disable-next-line prefer-destructuring
|
|
274
|
+
const { height: scrollableHeight } = scrollableElement.getClientRects()[0];
|
|
275
|
+
const activityOffsetTop = activityY + scrollableElement.scrollTop;
|
|
276
|
+
|
|
277
|
+
const scrollTop = Math.min(activityOffsetTop, activityOffsetTop - scrollableHeight + activityHeight);
|
|
278
|
+
|
|
279
|
+
scrollToBottomScrollTo(scrollTop, { behavior });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
[activityElementMapRef, getKeyByActivityId, rootElementRef, scrollToBottomScrollTo]
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
const scrollToEnd = useCallback(
|
|
287
|
+
() => scrollToBottomScrollToEnd({ behavior: 'smooth' }),
|
|
288
|
+
[scrollToBottomScrollToEnd]
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
const scrollRelative = useCallback(
|
|
292
|
+
(direction: 'down' | 'up', { displacement }: { displacement?: number } = {}) => {
|
|
293
|
+
const { current: rootElement } = rootElementRef;
|
|
294
|
+
|
|
295
|
+
if (!rootElement) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const scrollable: HTMLElement = rootElement.querySelector('.webchat__basic-transcript__scrollable');
|
|
300
|
+
let nextScrollTop: number;
|
|
301
|
+
|
|
302
|
+
if (typeof displacement === 'number') {
|
|
303
|
+
// eslint-disable-next-line no-magic-numbers
|
|
304
|
+
nextScrollTop = scrollable.scrollTop + (direction === 'down' ? 1 : -1) * displacement;
|
|
305
|
+
} else {
|
|
306
|
+
// eslint-disable-next-line no-magic-numbers
|
|
307
|
+
nextScrollTop = scrollable.scrollTop + (direction === 'down' ? 1 : -1) * scrollable.offsetHeight;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
scrollTo(
|
|
311
|
+
{
|
|
312
|
+
scrollTop: Math.max(0, Math.min(scrollable.scrollHeight - scrollable.offsetHeight, nextScrollTop))
|
|
313
|
+
},
|
|
314
|
+
{ behavior: 'smooth' }
|
|
315
|
+
);
|
|
316
|
+
},
|
|
317
|
+
[rootElementRef, scrollTo]
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
// Since there could be multiple instances of <BasicTranscript> inside the <Composer>, when the developer calls `scrollXXX`, we need to call it on all instances.
|
|
321
|
+
// We call `useRegisterScrollXXX` to register a callback function, the `useScrollXXX` will multiplex the call into each instance of <BasicTranscript>.
|
|
322
|
+
useRegisterScrollTo(scrollTo);
|
|
323
|
+
useRegisterScrollToEnd(scrollToEnd);
|
|
324
|
+
useRegisterScrollRelative(scrollRelative);
|
|
325
|
+
|
|
326
|
+
const markActivityKeyAsRead = useMarkActivityKeyAsRead();
|
|
327
|
+
|
|
328
|
+
const dispatchScrollPositionWithActivityId: (scrollPosition: ScrollToPosition) => void =
|
|
329
|
+
useDispatchScrollPosition();
|
|
330
|
+
|
|
331
|
+
// TODO: [P2] We should use IntersectionObserver to track what activity is in the scrollable.
|
|
332
|
+
// However, IntersectionObserver is not available on IE11, we need to make a limited polyfill in React style.
|
|
333
|
+
const handleScrollPosition = useCallback(
|
|
334
|
+
({ scrollTop }: { scrollTop: number }) => {
|
|
335
|
+
const { current: rootElement } = rootElementRef;
|
|
336
|
+
|
|
337
|
+
if (!rootElement) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const scrollableElement = rootElement.querySelector('.webchat__basic-transcript__scrollable');
|
|
342
|
+
|
|
343
|
+
// "getClientRects()" is not returning an array, thus, it is not destructurable.
|
|
344
|
+
// eslint-disable-next-line prefer-destructuring
|
|
345
|
+
const { bottom: scrollableClientBottom } = scrollableElement.getClientRects()[0];
|
|
346
|
+
|
|
347
|
+
// Find the activity just above scroll view bottom.
|
|
348
|
+
// If the scroll view is already on top, get the first activity.
|
|
349
|
+
const activityElements = Array.from(activityElementMapRef.current.entries());
|
|
350
|
+
const activityKeyJustAboveScrollBottom: string | undefined = (
|
|
351
|
+
scrollableElement.scrollTop
|
|
352
|
+
? activityElements
|
|
353
|
+
.reverse()
|
|
354
|
+
// Add subpixel tolerance
|
|
355
|
+
.find(([, element]) => element.getClientRects()[0].bottom < scrollableClientBottom + 1)
|
|
356
|
+
: activityElements[0]
|
|
357
|
+
)?.[0];
|
|
358
|
+
|
|
359
|
+
// When the end-user slowly scrolling the view down, we will mark activity as read when the message fully appear on the screen.
|
|
360
|
+
activityKeyJustAboveScrollBottom && markActivityKeyAsRead(activityKeyJustAboveScrollBottom);
|
|
361
|
+
|
|
362
|
+
if (dispatchScrollPositionWithActivityId) {
|
|
363
|
+
const activity = getActivityByKey(activityKeyJustAboveScrollBottom);
|
|
364
|
+
|
|
365
|
+
dispatchScrollPositionWithActivityId({ ...(activity ? { activityID: activity.id } : {}), scrollTop });
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
[
|
|
369
|
+
activityElementMapRef,
|
|
370
|
+
dispatchScrollPositionWithActivityId,
|
|
371
|
+
getActivityByKey,
|
|
372
|
+
markActivityKeyAsRead,
|
|
373
|
+
rootElementRef
|
|
374
|
+
]
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
useObserveScrollPosition(handleScrollPosition);
|
|
378
|
+
|
|
379
|
+
const handleTranscriptKeyDown = useCallback<KeyboardEventHandler<HTMLDivElement>>(
|
|
380
|
+
event => {
|
|
381
|
+
const { target } = event;
|
|
382
|
+
|
|
383
|
+
const fromEndOfTranscriptIndicator = target === terminatorRef.current;
|
|
384
|
+
const fromTranscript = target === event.currentTarget;
|
|
385
|
+
|
|
386
|
+
if (!fromEndOfTranscriptIndicator && !fromTranscript) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
let handled = true;
|
|
391
|
+
|
|
392
|
+
switch (event.key) {
|
|
393
|
+
case 'ArrowDown':
|
|
394
|
+
focusRelativeActivity(fromEndOfTranscriptIndicator ? 0 : 1);
|
|
395
|
+
break;
|
|
396
|
+
|
|
397
|
+
case 'ArrowUp':
|
|
398
|
+
// eslint-disable-next-line no-magic-numbers
|
|
399
|
+
focusRelativeActivity(fromEndOfTranscriptIndicator ? 0 : -1);
|
|
400
|
+
break;
|
|
401
|
+
|
|
402
|
+
case 'End':
|
|
403
|
+
focusRelativeActivity(Infinity);
|
|
404
|
+
break;
|
|
405
|
+
|
|
406
|
+
case 'Enter':
|
|
407
|
+
// This is capturing plain ENTER.
|
|
408
|
+
// When screen reader is not running, or screen reader is running outside of scan mode, the ENTER key will be captured here.
|
|
409
|
+
if (!fromEndOfTranscriptIndicator) {
|
|
410
|
+
const body: HTMLElement = activityElementMapRef.current
|
|
411
|
+
.get(focusedActivityKeyRef.current)
|
|
412
|
+
?.querySelector('.webchat__basic-transcript__activity-body');
|
|
413
|
+
|
|
414
|
+
tabbableElements(body)[0]?.focus();
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
break;
|
|
418
|
+
|
|
419
|
+
case 'Escape':
|
|
420
|
+
focus('sendBoxWithoutKeyboard');
|
|
421
|
+
break;
|
|
422
|
+
|
|
423
|
+
case 'Home':
|
|
424
|
+
focusRelativeActivity(-Infinity);
|
|
425
|
+
break;
|
|
426
|
+
|
|
427
|
+
default:
|
|
428
|
+
handled = false;
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (handled) {
|
|
433
|
+
event.preventDefault();
|
|
434
|
+
|
|
435
|
+
// 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.
|
|
436
|
+
event.stopPropagation();
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
[activityElementMapRef, focus, focusedActivityKeyRef, focusRelativeActivity, terminatorRef]
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
const handleTranscriptKeyDownCapture = useCallback<KeyboardEventHandler<HTMLDivElement>>(
|
|
443
|
+
event => {
|
|
444
|
+
const { altKey, ctrlKey, key, metaKey, target } = event;
|
|
445
|
+
|
|
446
|
+
if (altKey || (ctrlKey && key !== 'v') || metaKey || (!inputtableKey(key) && key !== 'Backspace')) {
|
|
447
|
+
// Ignore if one of the utility key (except SHIFT) is pressed
|
|
448
|
+
// E.g. CTRL-C on a link in one of the message should not jump to chat box
|
|
449
|
+
// E.g. "A" or "Backspace" should jump to chat box
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Send keystrokes to send box if we are focusing on the transcript or terminator.
|
|
454
|
+
if (target === event.currentTarget || target === terminatorRef.current) {
|
|
455
|
+
event.stopPropagation();
|
|
456
|
+
|
|
457
|
+
focus('sendBox');
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
[focus]
|
|
461
|
+
);
|
|
462
|
+
|
|
463
|
+
useRegisterFocusTranscript(useCallback(() => focusByActivityKey(undefined), [focusByActivityKey]));
|
|
464
|
+
|
|
465
|
+
// When the focusing activity has changed, dispatch an event to observers of "useObserveTranscriptFocus".
|
|
466
|
+
const dispatchTranscriptFocusByActivityKey = useDispatchTranscriptFocusByActivityKey();
|
|
467
|
+
|
|
468
|
+
// Dispatch a "transcript focus" event based on user selection.
|
|
469
|
+
// We should not dispatch "transcript focus" when a new activity come. Although the selection change, it is not initiated from the user.
|
|
470
|
+
useMemo(
|
|
471
|
+
() => dispatchTranscriptFocusByActivityKey(focusedExplicitly ? focusedActivityKey : undefined),
|
|
472
|
+
[dispatchTranscriptFocusByActivityKey, focusedActivityKey, focusedExplicitly]
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
// When the transcript is being focused on, we should dispatch a "transcriptfocus" event.
|
|
476
|
+
const handleFocus = useCallback(
|
|
477
|
+
// We call "focusByActivityKey" with activity key of "true".
|
|
478
|
+
// It means, tries to focus on anything.
|
|
479
|
+
({ currentTarget, target }) => target === currentTarget && focusByActivityKey(true, false),
|
|
480
|
+
[focusByActivityKey]
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
// This is required by IE11.
|
|
484
|
+
// 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">,
|
|
485
|
+
// despite the fact there are no "tabIndex" attributes set on the filler.
|
|
486
|
+
// We need to artificially send the focus back to the transcript.
|
|
487
|
+
const handleFocusFiller = useCallback(() => focusByActivityKey(undefined), [focusByActivityKey]);
|
|
488
|
+
|
|
489
|
+
// When focus into the transcript using TAB/SHIFT-TAB, scroll the focused activity into view.
|
|
490
|
+
useObserveFocusVisible(
|
|
491
|
+
rootElementRef,
|
|
492
|
+
useCallback(() => focusByActivityKey(undefined), [focusByActivityKey])
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
return (
|
|
496
|
+
<div
|
|
497
|
+
// Although Android TalkBack 12.1 does not support `aria-activedescendant`, when used, it become buggy and will narrate content twice.
|
|
498
|
+
// We are disabling `aria-activedescendant` for Android. See <ActivityRow> for details.
|
|
499
|
+
aria-activedescendant={android ? undefined : activeDescendantId}
|
|
500
|
+
aria-label={transcriptAriaLabel}
|
|
501
|
+
className={classNames(
|
|
502
|
+
'webchat__basic-transcript',
|
|
503
|
+
basicTranscriptStyleSet + '',
|
|
504
|
+
rootClassName,
|
|
505
|
+
(className || '') + ''
|
|
506
|
+
)}
|
|
507
|
+
dir={direction}
|
|
508
|
+
onFocus={handleFocus}
|
|
509
|
+
onKeyDown={handleTranscriptKeyDown}
|
|
510
|
+
onKeyDownCapture={handleTranscriptKeyDownCapture}
|
|
511
|
+
ref={callbackRef}
|
|
512
|
+
// "aria-activedescendant" will only works with a number of roles and it must be explicitly set.
|
|
513
|
+
// https://www.w3.org/TR/wai-aria/#aria-activedescendant
|
|
514
|
+
role="group"
|
|
515
|
+
// For up/down arrow key navigation across activities, this component must be included in the tab sequence.
|
|
516
|
+
// Otherwise, "aria-activedescendant" will not be narrated when the user press up/down arrow keys.
|
|
517
|
+
// https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_focus_activedescendant
|
|
518
|
+
tabIndex={0}
|
|
519
|
+
>
|
|
520
|
+
<LiveRegionTranscript activityElementMapRef={activityElementMapRef} />
|
|
521
|
+
{/* TODO: [P2] Fix ESLint error `no-use-before-define` */}
|
|
522
|
+
{/* eslint-disable-next-line @typescript-eslint/no-use-before-define */}
|
|
523
|
+
<InternalTranscriptScrollable onFocusFiller={handleFocusFiller} terminatorRef={terminatorRef}>
|
|
524
|
+
{renderingElements.map(
|
|
525
|
+
({
|
|
526
|
+
activity,
|
|
527
|
+
callbackRef,
|
|
528
|
+
hideTimestamp,
|
|
529
|
+
key,
|
|
530
|
+
renderActivity,
|
|
531
|
+
renderActivityStatus,
|
|
532
|
+
renderAvatar,
|
|
533
|
+
showCallout
|
|
534
|
+
}) => (
|
|
535
|
+
<ActivityRow activity={activity} key={key} ref={callbackRef}>
|
|
536
|
+
{renderActivity({
|
|
537
|
+
hideTimestamp,
|
|
538
|
+
renderActivityStatus,
|
|
539
|
+
renderAvatar,
|
|
540
|
+
showCallout
|
|
541
|
+
})}
|
|
542
|
+
</ActivityRow>
|
|
543
|
+
)
|
|
544
|
+
)}
|
|
545
|
+
</InternalTranscriptScrollable>
|
|
546
|
+
{!!renderingElements.length && (
|
|
547
|
+
<Fragment>
|
|
548
|
+
<FocusRedirector redirectRef={rootElementRef} />
|
|
549
|
+
<div
|
|
550
|
+
aria-hidden={true}
|
|
551
|
+
aria-labelledby={terminatorLabelId}
|
|
552
|
+
className="webchat__basic-transcript__terminator"
|
|
553
|
+
ref={terminatorRef}
|
|
554
|
+
role="note"
|
|
555
|
+
tabIndex={0}
|
|
556
|
+
>
|
|
557
|
+
<div className="webchat__basic-transcript__terminator-body">
|
|
558
|
+
{/* `id` is required for `aria-labelledby` */}
|
|
559
|
+
{/* eslint-disable-next-line react/forbid-dom-props */}
|
|
560
|
+
<div className="webchat__basic-transcript__terminator-text" id={terminatorLabelId}>
|
|
561
|
+
{terminatorText}
|
|
562
|
+
</div>
|
|
563
|
+
</div>
|
|
564
|
+
</div>
|
|
565
|
+
</Fragment>
|
|
566
|
+
)}
|
|
567
|
+
<div className="webchat__basic-transcript__focus-indicator" />
|
|
568
|
+
</div>
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
InternalTranscript.defaultProps = {
|
|
574
|
+
className: ''
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
InternalTranscript.propTypes = {
|
|
578
|
+
// PropTypes cannot validate precisely with its TypeScript counterpart.
|
|
579
|
+
// @ts-ignore
|
|
580
|
+
activityElementMapRef: PropTypes.shape({
|
|
581
|
+
current: PropTypes.instanceOf(Map)
|
|
582
|
+
}).isRequired,
|
|
583
|
+
className: PropTypes.string
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
type InternalTranscriptScrollableProps = {
|
|
587
|
+
children?: ReactNode;
|
|
588
|
+
onFocusFiller: () => void;
|
|
589
|
+
terminatorRef: MutableRefObject<HTMLDivElement>;
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
// Separating high-frequency hooks to improve performance.
|
|
593
|
+
const InternalTranscriptScrollable: FC<InternalTranscriptScrollableProps> = ({
|
|
594
|
+
children,
|
|
595
|
+
onFocusFiller,
|
|
596
|
+
terminatorRef
|
|
597
|
+
}) => {
|
|
598
|
+
const [{ activities: activitiesStyleSet }] = useStyleSet();
|
|
599
|
+
const [animatingToEnd]: [boolean] = useAnimatingToEnd();
|
|
600
|
+
const [atEnd]: [boolean] = useAtEnd();
|
|
601
|
+
const [, unreadActivityKeys] = useActivityKeysByRead();
|
|
602
|
+
const [sticky]: [boolean] = useSticky();
|
|
603
|
+
const [styleOptions] = useStyleOptions();
|
|
604
|
+
const focusByActivityKey = useFocusByActivityKey();
|
|
605
|
+
const localize = useLocalizer();
|
|
606
|
+
const markActivityKeyAsRead = useMarkActivityKeyAsRead();
|
|
607
|
+
const markAllAsAcknowledged = useMarkAllAsAcknowledged();
|
|
608
|
+
const scrollToEnd: (options?: ScrollToOptions) => void = useScrollToEnd();
|
|
609
|
+
|
|
610
|
+
const prevSticky = usePrevious(sticky);
|
|
611
|
+
const transcriptRoleDescription = localize('TRANSCRIPT_ARIA_ROLE_ALT');
|
|
612
|
+
|
|
613
|
+
const stickyChangedToTrue = prevSticky !== sticky && sticky;
|
|
614
|
+
|
|
615
|
+
// Acknowledged means either:
|
|
616
|
+
// 1. The user sent a message
|
|
617
|
+
// - We don't need a condition here. When Web Chat sends the user's message, it will scroll to bottom, and it will trigger condition 2 below.
|
|
618
|
+
// 2. The user scroll to the bottom of the transcript, from a non-bottom scroll position
|
|
619
|
+
// - If the transcript is already at the bottom, the user needs to scroll up and then go back down
|
|
620
|
+
// - What happens if we are relaxing "scrolled from a non-bottom scroll position":
|
|
621
|
+
// 1. The condition will become solely "at the bottom of the transcript"
|
|
622
|
+
// 2. Auto-scroll will always scroll the transcript to the bottom
|
|
623
|
+
// 3. Web Chat will always acknowledge all activities as it is at the bottom
|
|
624
|
+
// 4. Acknowledge flag become useless
|
|
625
|
+
// 5. Therefore, even the developer set "pause after 3 activities", if activities are coming in at a slow pace (not batched in a single render)
|
|
626
|
+
// Web Chat will keep scrolling and not snapped/paused
|
|
627
|
+
|
|
628
|
+
// Note: When Web Chat is loaded, there are no activities acknowledged. We need to assume all arriving activities are acknowledged until end-user sends their first activity.
|
|
629
|
+
// Activities loaded initially could be from conversation history. Without assuming acknowledgement, Web Chat will not scroll initially (as everything is not acknowledged).
|
|
630
|
+
// It would be better if the chat adapter should let Web Chat know if the activity is loaded from history or not.
|
|
631
|
+
|
|
632
|
+
// TODO: [P2] #3670 Move the "conversation history acknowledgement" logic mentioned above to polyfill of chat adapters.
|
|
633
|
+
// 1. Chat adapter should send "acknowledged" as part of "channelData"
|
|
634
|
+
// 2. If "acknowledged" is "undefined", we set it to:
|
|
635
|
+
// a. true, if there are no egress activities yet
|
|
636
|
+
// b. Otherwise, false
|
|
637
|
+
|
|
638
|
+
useMemo(
|
|
639
|
+
() =>
|
|
640
|
+
stickyChangedToTrue &&
|
|
641
|
+
// TODO: [P2] Both `markActivityKeyAsRead` and `markAllAsAcknowledged` hook are setters of useState.
|
|
642
|
+
// This means, in a render loop, we will be calling setter and will cause another re-render.
|
|
643
|
+
// This is not trivial but we should think if there is a way to avoid this.
|
|
644
|
+
markAllAsAcknowledged(),
|
|
645
|
+
[markAllAsAcknowledged, stickyChangedToTrue]
|
|
646
|
+
);
|
|
647
|
+
|
|
648
|
+
const [flattenedActivityTreeWithRenderer] = useActivityTreeWithRenderer({ flat: true });
|
|
649
|
+
const getKeyByActivity = useGetKeyByActivity();
|
|
650
|
+
|
|
651
|
+
const renderingActivityKeys: string[] = useMemo<string[]>(
|
|
652
|
+
() => flattenedActivityTreeWithRenderer.map(({ activity }) => getKeyByActivity(activity)),
|
|
653
|
+
[flattenedActivityTreeWithRenderer, getKeyByActivity]
|
|
654
|
+
);
|
|
655
|
+
|
|
656
|
+
const renderingActivityKeysRef = useValueRef(renderingActivityKeys);
|
|
657
|
+
|
|
658
|
+
// To prevent flashy button, we are not waiting for another render loop to update the `[readActivityKeys, unreadActivityKeys]` state.
|
|
659
|
+
// Instead, we are building the next one in this `useMemo` call.
|
|
660
|
+
const nextUnreadActivityKeys = useMemo(() => {
|
|
661
|
+
// This code need to be careful reviewed as it will cause another render. The code should be converging.
|
|
662
|
+
// After we call `markActivityKeyAsRead`, everything will be read and nothing will be unread.
|
|
663
|
+
// That means, in next render, `unreadActivityKeys` will be emptied and the `markActivityKeyAsRead` will not get called again.
|
|
664
|
+
if (sticky && unreadActivityKeys.length) {
|
|
665
|
+
markActivityKeyAsRead(unreadActivityKeys[unreadActivityKeys.length - 1]);
|
|
666
|
+
|
|
667
|
+
return [];
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return unreadActivityKeys;
|
|
671
|
+
}, [markActivityKeyAsRead, sticky, unreadActivityKeys]);
|
|
672
|
+
|
|
673
|
+
const nextUnreadActivityKeysRef = useValueRef(nextUnreadActivityKeys);
|
|
674
|
+
|
|
675
|
+
// If we are rendering anything that is unread, we should show the "New messages" button.
|
|
676
|
+
// Not everything in the `unreadActivityKeys` are rendered, say, bot typing indicator.
|
|
677
|
+
// We should not show the "New messages" button for bot typing indicator as it will confuse the user.
|
|
678
|
+
const unread = useMemo(
|
|
679
|
+
() => nextUnreadActivityKeys.some(key => renderingActivityKeys.includes(key)),
|
|
680
|
+
[renderingActivityKeys, nextUnreadActivityKeys]
|
|
681
|
+
);
|
|
682
|
+
|
|
683
|
+
const handleScrollToEndButtonClick = useCallback(() => {
|
|
684
|
+
scrollToEnd({ behavior: 'smooth' });
|
|
685
|
+
|
|
686
|
+
const { current: renderingActivityKeys } = renderingActivityKeysRef;
|
|
687
|
+
|
|
688
|
+
// After the "New message" button is clicked, focus on the first unread activity which will be rendered.
|
|
689
|
+
const firstUnreadRenderingActivityKey = nextUnreadActivityKeysRef.current.find(key =>
|
|
690
|
+
renderingActivityKeys.includes(key)
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
if (firstUnreadRenderingActivityKey) {
|
|
694
|
+
focusByActivityKey(firstUnreadRenderingActivityKey);
|
|
695
|
+
} else {
|
|
696
|
+
// If no unread activity, send the focus to the terminator block.
|
|
697
|
+
terminatorRef.current?.focus();
|
|
698
|
+
}
|
|
699
|
+
}, [focusByActivityKey, nextUnreadActivityKeysRef, renderingActivityKeysRef, scrollToEnd, terminatorRef]);
|
|
700
|
+
|
|
701
|
+
const renderScrollToEndButton = useCreateScrollToEndButtonRenderer()({
|
|
702
|
+
atEnd: animatingToEnd || atEnd || sticky,
|
|
703
|
+
styleOptions,
|
|
704
|
+
unread
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
return (
|
|
708
|
+
<React.Fragment>
|
|
709
|
+
{renderScrollToEndButton && renderScrollToEndButton({ onClick: handleScrollToEndButtonClick })}
|
|
710
|
+
{!!React.Children.count(children) && <FocusRedirector redirectRef={terminatorRef} />}
|
|
711
|
+
<ReactScrollToBottomPanel className="webchat__basic-transcript__scrollable">
|
|
712
|
+
<div aria-hidden={true} className="webchat__basic-transcript__filler" onFocus={onFocusFiller} />
|
|
713
|
+
<section
|
|
714
|
+
aria-roledescription={transcriptRoleDescription}
|
|
715
|
+
className={classNames(activitiesStyleSet + '', 'webchat__basic-transcript__transcript')}
|
|
716
|
+
role="feed"
|
|
717
|
+
>
|
|
718
|
+
{children}
|
|
719
|
+
</section>
|
|
720
|
+
<BasicTypingIndicator />
|
|
721
|
+
</ReactScrollToBottomPanel>
|
|
722
|
+
</React.Fragment>
|
|
723
|
+
);
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
InternalTranscriptScrollable.propTypes = {
|
|
727
|
+
children: PropTypes.any.isRequired,
|
|
728
|
+
onFocusFiller: PropTypes.func.isRequired,
|
|
729
|
+
terminatorRef: PropTypes.any.isRequired
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
type Scroller = ({ offsetHeight, scrollTop }: { offsetHeight: number; scrollTop: number }) => number;
|
|
733
|
+
|
|
734
|
+
// "scroller" is the auto-scroll limiter, a.k.a. auto scroll snap.
|
|
735
|
+
const useScroller = (activityElementMapRef: MutableRefObject<ActivityElementMap>): Scroller => {
|
|
736
|
+
const [activityKeys] = useActivityKeys();
|
|
737
|
+
const [lastAcknowledgedActivityKey] = useLastAcknowledgedActivityKey();
|
|
738
|
+
const [styleOptions] = useStyleOptions();
|
|
739
|
+
|
|
740
|
+
const activityKeysRef = useValueRef(activityKeys);
|
|
741
|
+
const lastAcknowledgedActivityKeyRef = useValueRef(lastAcknowledgedActivityKey);
|
|
742
|
+
const styleOptionsRef = useValueRef(styleOptions);
|
|
743
|
+
|
|
744
|
+
return useCallback(
|
|
745
|
+
({ offsetHeight, scrollTop }) => {
|
|
746
|
+
const {
|
|
747
|
+
current: {
|
|
748
|
+
autoScrollSnapOnActivity,
|
|
749
|
+
autoScrollSnapOnActivityOffset,
|
|
750
|
+
autoScrollSnapOnPage,
|
|
751
|
+
autoScrollSnapOnPageOffset
|
|
752
|
+
}
|
|
753
|
+
} = styleOptionsRef;
|
|
754
|
+
|
|
755
|
+
const patchedAutoScrollSnapOnActivity =
|
|
756
|
+
typeof autoScrollSnapOnActivity === 'number'
|
|
757
|
+
? Math.max(0, autoScrollSnapOnActivity)
|
|
758
|
+
: autoScrollSnapOnActivity
|
|
759
|
+
? 1
|
|
760
|
+
: 0;
|
|
761
|
+
const patchedAutoScrollSnapOnPage =
|
|
762
|
+
typeof autoScrollSnapOnPage === 'number'
|
|
763
|
+
? Math.max(0, Math.min(1, autoScrollSnapOnPage))
|
|
764
|
+
: autoScrollSnapOnPage
|
|
765
|
+
? 1
|
|
766
|
+
: 0;
|
|
767
|
+
const patchedAutoScrollSnapOnActivityOffset =
|
|
768
|
+
typeof autoScrollSnapOnActivityOffset === 'number' ? autoScrollSnapOnActivityOffset : 0;
|
|
769
|
+
const patchedAutoScrollSnapOnPageOffset =
|
|
770
|
+
typeof autoScrollSnapOnPageOffset === 'number' ? autoScrollSnapOnPageOffset : 0;
|
|
771
|
+
|
|
772
|
+
if (patchedAutoScrollSnapOnActivity || patchedAutoScrollSnapOnPage) {
|
|
773
|
+
const { current: activityElementMap } = activityElementMapRef;
|
|
774
|
+
const { current: activityKeys } = activityKeysRef;
|
|
775
|
+
const { current: lastAcknowledgedActivityKey } = lastAcknowledgedActivityKeyRef;
|
|
776
|
+
const values: number[] = [];
|
|
777
|
+
|
|
778
|
+
const lastAcknowledgedActivityKeyIndex = activityKeys.indexOf(lastAcknowledgedActivityKey);
|
|
779
|
+
|
|
780
|
+
if (~lastAcknowledgedActivityKeyIndex) {
|
|
781
|
+
// The activity that we acknowledged could be not rendered, such as post back activity.
|
|
782
|
+
// When calculating scroll snap, we can only base on the first unacknowledged-and-rendering activity.
|
|
783
|
+
const renderingActivityKeys = Array.from(activityElementMap.keys());
|
|
784
|
+
let firstUnacknowledgedActivityElementIndex = -1;
|
|
785
|
+
|
|
786
|
+
for (const acknowledgedActivityKey of activityKeys.slice(0, lastAcknowledgedActivityKeyIndex + 1).reverse()) {
|
|
787
|
+
const index = renderingActivityKeys.indexOf(acknowledgedActivityKey);
|
|
788
|
+
|
|
789
|
+
if (~index) {
|
|
790
|
+
if (index !== renderingActivityKeys.length - 1) {
|
|
791
|
+
firstUnacknowledgedActivityElementIndex = index + 1;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
break;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (~firstUnacknowledgedActivityElementIndex) {
|
|
799
|
+
const activityElements = Array.from(activityElementMap.values());
|
|
800
|
+
|
|
801
|
+
if (patchedAutoScrollSnapOnActivity) {
|
|
802
|
+
// Gets the activity element which we should snap to.
|
|
803
|
+
const nthUnacknowledgedActivityElement =
|
|
804
|
+
activityElements[firstUnacknowledgedActivityElementIndex + patchedAutoScrollSnapOnActivity - 1];
|
|
805
|
+
|
|
806
|
+
if (nthUnacknowledgedActivityElement) {
|
|
807
|
+
const nthUnacknowledgedActivityBoundingBoxElement = nthUnacknowledgedActivityElement?.querySelector(
|
|
808
|
+
'.webchat__basic-transcript__activity-active-descendant'
|
|
809
|
+
) as HTMLElement;
|
|
810
|
+
const nthUnacknowledgedActivityOffsetTop =
|
|
811
|
+
nthUnacknowledgedActivityElement.offsetTop + nthUnacknowledgedActivityBoundingBoxElement.offsetTop;
|
|
812
|
+
|
|
813
|
+
values.push(
|
|
814
|
+
nthUnacknowledgedActivityOffsetTop +
|
|
815
|
+
nthUnacknowledgedActivityBoundingBoxElement.offsetHeight -
|
|
816
|
+
offsetHeight -
|
|
817
|
+
scrollTop +
|
|
818
|
+
patchedAutoScrollSnapOnActivityOffset
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
if (patchedAutoScrollSnapOnPage) {
|
|
824
|
+
const firstUnacknowledgedActivityElement = activityElements[+firstUnacknowledgedActivityElementIndex];
|
|
825
|
+
const firstUnacknowledgedActivityBoundingBoxElement = firstUnacknowledgedActivityElement.querySelector(
|
|
826
|
+
'.webchat__basic-transcript__activity-active-descendant'
|
|
827
|
+
) as HTMLElement;
|
|
828
|
+
const firstUnacknowledgedActivityOffsetTop =
|
|
829
|
+
firstUnacknowledgedActivityElement.offsetTop + firstUnacknowledgedActivityBoundingBoxElement.offsetTop;
|
|
830
|
+
|
|
831
|
+
values.push(
|
|
832
|
+
firstUnacknowledgedActivityOffsetTop -
|
|
833
|
+
scrollTop -
|
|
834
|
+
offsetHeight * (1 - patchedAutoScrollSnapOnPage) +
|
|
835
|
+
patchedAutoScrollSnapOnPageOffset
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
return Math.min(...values);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
return Infinity;
|
|
845
|
+
},
|
|
846
|
+
[activityElementMapRef, activityKeysRef, lastAcknowledgedActivityKeyRef, styleOptionsRef]
|
|
847
|
+
);
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
type BasicTranscriptProps = {
|
|
851
|
+
className?: string;
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
const BasicTranscript: VFC<BasicTranscriptProps> = ({ className }) => {
|
|
855
|
+
const activityElementMapRef = useRef<ActivityElementMap>(new Map());
|
|
856
|
+
const containerRef = useRef<HTMLDivElement>();
|
|
857
|
+
|
|
858
|
+
const scroller = useScroller(activityElementMapRef);
|
|
859
|
+
|
|
860
|
+
return (
|
|
861
|
+
<TranscriptFocusComposer containerRef={containerRef}>
|
|
862
|
+
<ReactScrollToBottomComposer scroller={scroller}>
|
|
863
|
+
<KeyboardHelp />
|
|
864
|
+
<InternalTranscript activityElementMapRef={activityElementMapRef} className={className} ref={containerRef} />
|
|
865
|
+
</ReactScrollToBottomComposer>
|
|
866
|
+
</TranscriptFocusComposer>
|
|
867
|
+
);
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
BasicTranscript.defaultProps = {
|
|
871
|
+
className: ''
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
BasicTranscript.propTypes = {
|
|
875
|
+
className: PropTypes.string
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
export default BasicTranscript;
|