botframework-webchat-component 4.14.1 → 4.15.2-main.20220413.af6e8a3
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 -106
- package/.prettierrc.yml +1 -1
- package/lib/Activity/CarouselFilmStrip.js +1 -1
- package/lib/Activity/CarouselFilmStripAttachment.js +1 -1
- package/lib/Activity/CarouselLayout.js +3 -3
- package/lib/Activity/Speak.d.ts +2 -2
- package/lib/Activity/Speak.d.ts.map +1 -1
- package/lib/Activity/Speak.js +15 -8
- package/lib/Activity/StackedLayout.d.ts +5 -3
- package/lib/Activity/StackedLayout.d.ts.map +1 -1
- package/lib/Activity/StackedLayout.js +16 -21
- package/lib/Attachment/FileAttachment.js +2 -2
- package/lib/Attachment/FileContent.d.ts.map +1 -1
- package/lib/Attachment/FileContent.js +11 -1
- package/lib/BasicSendBox.d.ts.map +1 -1
- package/lib/BasicSendBox.js +8 -9
- package/lib/BasicToast.js +1 -1
- package/lib/BasicToaster.js +6 -3
- package/lib/BasicTranscript.d.ts +7 -0
- package/lib/BasicTranscript.d.ts.map +1 -0
- package/lib/BasicTranscript.js +386 -728
- package/lib/BasicTypingIndicator.d.ts +2 -1
- package/lib/BasicTypingIndicator.d.ts.map +1 -1
- package/lib/BasicTypingIndicator.js +12 -4
- package/lib/Composer.d.ts.map +1 -1
- package/lib/Composer.js +37 -32
- package/lib/ConnectivityStatus/Connecting.js +1 -1
- package/lib/Dictation.js +1 -1
- package/lib/Middleware/Activity/createCoreMiddleware.d.ts.map +1 -1
- package/lib/Middleware/Activity/createCoreMiddleware.js +9 -12
- package/lib/Middleware/ActivityStatus/SendStatus/SendFailedRetry.js +1 -1
- package/lib/Middleware/ActivityStatus/SendStatus/SendStatus.d.ts +2 -2
- package/lib/Middleware/ActivityStatus/SendStatus/SendStatus.d.ts.map +1 -1
- package/lib/Middleware/ActivityStatus/SendStatus/SendStatus.js +5 -4
- package/lib/Middleware/ActivityStatus/Timestamp.d.ts +2 -2
- package/lib/Middleware/ActivityStatus/Timestamp.d.ts.map +1 -1
- package/lib/Middleware/ActivityStatus/Timestamp.js +4 -2
- package/lib/Middleware/ActivityStatus/createSendStatusMiddleware.js +20 -17
- package/lib/Middleware/Attachment/createCoreMiddleware.js +2 -5
- package/lib/Middleware/AttachmentForScreenReader/createCoreMiddleware.d.ts.map +1 -1
- package/lib/Middleware/AttachmentForScreenReader/createCoreMiddleware.js +1 -1
- package/lib/Middleware/Avatar/createCoreMiddleware.d.ts.map +1 -1
- package/lib/Middleware/Avatar/createCoreMiddleware.js +2 -3
- package/lib/Middleware/CardAction/createCoreMiddleware.js +10 -3
- package/lib/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.d.ts.map +1 -1
- package/lib/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.js +1 -1
- package/lib/Middleware/Toast/createCoreMiddleware.d.ts.map +1 -1
- package/lib/Middleware/Toast/createCoreMiddleware.js +1 -1
- package/lib/Middleware/TypingIndicator/createCoreMiddleware.d.ts.map +1 -1
- package/lib/Middleware/TypingIndicator/createCoreMiddleware.js +8 -6
- package/lib/ReactWebChat.js +2 -2
- package/lib/ScreenReaderActivity.js +15 -6
- package/lib/ScreenReaderText.d.ts +9 -0
- package/lib/ScreenReaderText.d.ts.map +1 -0
- package/lib/ScreenReaderText.js +22 -8
- package/lib/SendBox/AutoResizeTextArea.js +1 -1
- package/lib/SendBox/DictationInterims.js +1 -1
- package/lib/SendBox/IconButton.d.ts +11 -0
- package/lib/SendBox/IconButton.d.ts.map +1 -0
- package/lib/SendBox/IconButton.js +26 -6
- package/lib/SendBox/MicrophoneButton.js +2 -2
- package/lib/SendBox/SendButton.js +2 -2
- package/lib/SendBox/SuggestedAction.d.ts +17 -0
- package/lib/SendBox/SuggestedAction.d.ts.map +1 -0
- package/lib/SendBox/SuggestedAction.js +26 -17
- package/lib/SendBox/SuggestedActions.d.ts.map +1 -1
- package/lib/SendBox/SuggestedActions.js +26 -9
- package/lib/SendBox/TextBox.js +4 -4
- package/lib/SendBox/UploadButton.js +4 -4
- package/lib/Styles/StyleSet/BasicTranscript.d.ts +13 -5
- package/lib/Styles/StyleSet/BasicTranscript.d.ts.map +1 -1
- package/lib/Styles/StyleSet/BasicTranscript.js +21 -9
- package/lib/Styles/StyleSet/Bubble.d.ts.map +1 -1
- package/lib/Styles/StyleSet/Bubble.js +3 -3
- package/lib/Styles/StyleSet/CarouselFilmStrip.d.ts.map +1 -1
- package/lib/Styles/StyleSet/CarouselFilmStrip.js +3 -3
- package/lib/Styles/StyleSet/CarouselFilmStripAttachment.d.ts.map +1 -1
- package/lib/Styles/StyleSet/CarouselFilmStripAttachment.js +3 -3
- 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/ScrollToEndButton.d.ts +1 -1
- package/lib/Styles/StyleSet/ScrollToEndButton.js +2 -2
- package/lib/Styles/StyleSet/SendBoxButton.d.ts +56 -10
- package/lib/Styles/StyleSet/SendBoxButton.d.ts.map +1 -1
- package/lib/Styles/StyleSet/SendBoxButton.js +76 -12
- package/lib/Styles/StyleSet/SendBoxTextBox.d.ts +1 -1
- package/lib/Styles/StyleSet/SendBoxTextBox.d.ts.map +1 -1
- package/lib/Styles/StyleSet/SendBoxTextBox.js +3 -3
- package/lib/Styles/StyleSet/StackedLayout.d.ts.map +1 -1
- package/lib/Styles/StyleSet/StackedLayout.js +1 -1
- package/lib/Styles/StyleSet/SuggestedAction.d.ts +73 -28
- package/lib/Styles/StyleSet/SuggestedAction.d.ts.map +1 -1
- package/lib/Styles/StyleSet/SuggestedAction.js +116 -41
- package/lib/Styles/StyleSet/SuggestedActions.d.ts +13 -6
- package/lib/Styles/StyleSet/SuggestedActions.d.ts.map +1 -1
- package/lib/Styles/StyleSet/SuggestedActions.js +13 -6
- package/lib/Styles/createStyleSet.d.ts +324 -55
- package/lib/Styles/createStyleSet.d.ts.map +1 -1
- package/lib/Styles/createStyleSet.js +6 -3
- package/lib/Styles/mirrorStyle.js +12 -3
- package/lib/Toast/NotificationIcon.js +7 -3
- package/lib/Toast/createToastMiddleware.d.ts.map +1 -1
- package/lib/Toast/createToastMiddleware.js +1 -1
- package/lib/Transcript/ActivityRow.d.ts +9 -0
- package/lib/Transcript/ActivityRow.d.ts.map +1 -0
- package/lib/Transcript/ActivityRow.js +159 -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 +229 -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/Transcript/useTypistNames.d.ts +3 -0
- package/lib/Transcript/useTypistNames.d.ts.map +1 -0
- package/lib/Transcript/useTypistNames.js +61 -0
- package/lib/Utils/AccessKeySink/Surface.js +1 -1
- package/lib/Utils/AccessibleButton.d.ts +11 -0
- package/lib/Utils/AccessibleButton.d.ts.map +1 -0
- package/lib/Utils/AccessibleButton.js +12 -7
- package/lib/Utils/AccessibleInputText.js +1 -1
- package/lib/Utils/AccessibleTextArea.js +1 -1
- package/lib/Utils/CroppedImage.js +1 -1
- package/lib/Utils/FocusRedirector.d.ts +9 -0
- package/lib/Utils/FocusRedirector.d.ts.map +1 -0
- package/lib/Utils/FocusRedirector.js +17 -14
- package/lib/Utils/InlineMarkdown.js +15 -8
- package/lib/Utils/TypeFocusSink/FocusBox.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/activityAltText.d.ts +8 -0
- package/lib/Utils/activityAltText.d.ts.map +1 -0
- package/lib/Utils/addTargetBlankToHyperlinksMarkdown.js +2 -2
- package/lib/Utils/createCustomEvent.js +8 -2
- package/lib/Utils/detectBrowser.js +4 -2
- package/lib/Utils/downscaleImageToDataURL/downscaleImageToDataURLUsingWorker.js +1 -1
- package/lib/Utils/downscaleImageToDataURL/index.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 +9 -2
- package/lib/Utils/readDataURIToBlob.js +1 -1
- 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/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 +15 -7
- package/lib/hooks/index.js +24 -24
- package/lib/hooks/internal/BypassSpeechSynthesisPonyfill.js +8 -7
- 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/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/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 +11 -2
- 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/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/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/useFocus.d.ts +1 -1
- package/lib/hooks/useFocus.d.ts.map +1 -1
- package/lib/hooks/useFocus.js +1 -1
- package/lib/hooks/useObserveTranscriptFocus.d.ts +2 -2
- package/lib/hooks/useObserveTranscriptFocus.d.ts.map +1 -1
- package/lib/hooks/useObserveTranscriptFocus.js +1 -1
- package/lib/hooks/useRenderMarkdownAsHTML.d.ts.map +1 -1
- package/lib/hooks/useRenderMarkdownAsHTML.js +1 -1
- package/lib/hooks/useScrollTo.d.ts.map +1 -1
- package/lib/hooks/useScrollTo.js +1 -1
- package/lib/hooks/useScrollToEnd.d.ts.map +1 -1
- package/lib/hooks/useScrollToEnd.js +1 -1
- package/lib/hooks/useSendFiles.d.ts.map +1 -1
- package/lib/hooks/useSendFiles.js +4 -4
- package/lib/index.d.ts +13 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +20 -18
- 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 +37 -0
- package/lib/providers/LiveRegionTwin/LiveRegionTwinComposer.d.ts.map +1 -0
- package/lib/providers/LiveRegionTwin/LiveRegionTwinComposer.js +160 -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 +12 -0
- package/lib/providers/LiveRegionTwin/private/LiveRegionTwinContainer.d.ts.map +1 -0
- package/lib/providers/LiveRegionTwin/private/LiveRegionTwinContainer.js +100 -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/package.json +30 -33
- package/src/Activity/CarouselFilmStripAttachment.js +0 -1
- package/src/Activity/Speak.tsx +21 -18
- package/src/Activity/StackedLayout.tsx +30 -35
- package/src/Attachment/FileAttachment.js +1 -1
- package/src/Attachment/FileContent.tsx +12 -0
- package/src/BasicSendBox.tsx +3 -2
- package/src/BasicToaster.js +5 -1
- package/src/BasicTranscript.tsx +894 -0
- package/src/BasicTypingIndicator.tsx +3 -2
- package/src/Composer.tsx +41 -29
- package/src/Middleware/Activity/createCoreMiddleware.tsx +47 -46
- package/src/Middleware/ActivityStatus/SendStatus/SendStatus.tsx +5 -3
- package/src/Middleware/ActivityStatus/Timestamp.tsx +5 -3
- package/src/Middleware/ActivityStatus/createSendStatusMiddleware.js +2 -0
- package/src/Middleware/Attachment/createCoreMiddleware.tsx +28 -28
- package/src/Middleware/AttachmentForScreenReader/createCoreMiddleware.tsx +25 -23
- package/src/Middleware/Avatar/createCoreMiddleware.tsx +9 -8
- package/src/Middleware/CardAction/createCoreMiddleware.js +9 -2
- package/src/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.ts +11 -9
- package/src/Middleware/Toast/createCoreMiddleware.tsx +12 -10
- package/src/Middleware/TypingIndicator/createCoreMiddleware.tsx +8 -2
- package/src/ScreenReaderActivity.js +18 -3
- package/src/{ScreenReaderText.js → ScreenReaderText.tsx} +27 -9
- package/src/SendBox/{IconButton.js → IconButton.tsx} +19 -3
- package/src/SendBox/{SuggestedAction.js → SuggestedAction.tsx} +72 -35
- package/src/SendBox/SuggestedActions.tsx +48 -21
- package/src/Styles/StyleSet/BasicTranscript.ts +34 -20
- package/src/Styles/StyleSet/Bubble.ts +0 -1
- package/src/Styles/StyleSet/CarouselFilmStrip.ts +12 -10
- package/src/Styles/StyleSet/CarouselFilmStripAttachment.ts +4 -3
- package/src/Styles/StyleSet/KeyboardHelp.ts +157 -0
- package/src/Styles/StyleSet/ScrollToEndButton.ts +1 -1
- 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 +129 -42
- package/src/Styles/StyleSet/SuggestedActions.ts +13 -5
- package/src/Styles/createStyleSet.ts +2 -1
- package/src/Styles/mirrorStyle.js +10 -2
- package/src/Toast/NotificationIcon.js +4 -1
- package/src/Toast/createToastMiddleware.tsx +4 -1
- package/src/Transcript/ActivityRow.tsx +123 -0
- package/src/Transcript/ActivityTextAlt.tsx +31 -0
- package/src/Transcript/FocusTrap.tsx +64 -0
- package/src/Transcript/KeyboardHelp.tsx +282 -0
- package/src/Transcript/LiveRegionTranscript.tsx +196 -0
- package/src/Transcript/types.ts +1 -0
- package/src/Transcript/useActivityAccessibleName.ts +84 -0
- package/src/Transcript/useTypistNames.ts +37 -0
- package/src/Utils/{AccessibleButton.js → AccessibleButton.tsx} +19 -4
- package/src/Utils/{FocusRedirector.js → FocusRedirector.tsx} +21 -8
- package/src/Utils/InlineMarkdown.js +18 -2
- package/src/Utils/TypeFocusSink/inputtableKey.ts +5 -1
- 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.ts +14 -0
- package/src/Utils/isZeroOrPositive.ts +7 -0
- package/src/Utils/mapMap.js +7 -1
- package/src/Utils/scrollIntoViewWithBlockNearest.ts +20 -0
- package/src/Utils/shallowEquals.js +8 -1
- package/src/Utils/supportPseudoClass.ts +17 -0
- package/src/Utils/{tabbableElements.js → tabbableElements.ts} +6 -5
- package/src/connectToWebChat.js +11 -4
- package/src/hooks/internal/BypassSpeechSynthesisPonyfill.js +3 -1
- 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 +1 -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/useStateRef.ts +31 -0
- package/src/hooks/internal/useValueRef.ts +22 -0
- package/src/hooks/useFocus.ts +1 -1
- package/src/hooks/useObserveTranscriptFocus.ts +2 -2
- package/src/hooks/useRenderMarkdownAsHTML.ts +4 -5
- package/src/hooks/useScrollTo.ts +4 -3
- package/src/hooks/useScrollToEnd.ts +4 -3
- package/src/hooks/useSendFiles.ts +7 -5
- package/src/index.ts +0 -1
- 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 +64 -0
- package/src/providers/ActivityTree/private/useActivityTreeWithRenderer.ts +135 -0
- package/src/providers/ActivityTree/private/useContext.ts +15 -0
- package/src/providers/ActivityTree/useActivityTreeWithRenderer.ts +16 -0
- package/src/providers/LiveRegionTwin/LiveRegionTwinComposer.tsx +161 -0
- package/src/providers/LiveRegionTwin/private/Context.ts +15 -0
- package/src/providers/LiveRegionTwin/private/LiveRegionTwinContainer.tsx +80 -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/.eslintignore +0 -1
- package/lib/Middleware/GroupActivities/createCoreMiddleware.js +0 -69
- package/lib/Utils/findLastIndex.js +0 -32
- package/lib/hooks/internal/useAcknowledgedActivity.js +0 -90
- package/lib/hooks/internal/useDispatchTranscriptFocus.js +0 -19
- package/src/BasicTranscript.js +0 -1139
- package/src/Middleware/GroupActivities/createCoreMiddleware.js +0 -57
- package/src/Utils/findLastIndex.js +0 -11
- package/src/Utils/findLastIndex.spec.js +0 -31
- package/src/Utils/getActivityUniqueId.js +0 -3
- package/src/Utils/intersectionOf.js +0 -11
- package/src/Utils/isZeroOrPositive.js +0 -4
- 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
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { ActivityWithRenderer, ReadonlyActivityTree } from './types';
|
|
4
|
+
|
|
5
|
+
type ActivityTreeContextType = {
|
|
6
|
+
activityTreeWithRendererState: readonly [ReadonlyActivityTree];
|
|
7
|
+
flattenedActivityTreeWithRendererState: readonly [readonly ActivityWithRenderer[]];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default createContext<ActivityTreeContextType>(undefined);
|
|
11
|
+
|
|
12
|
+
export type { ActivityTreeContextType };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ActivityComponentFactory } from 'botframework-webchat-api';
|
|
2
|
+
import type { WebChatActivity } from 'botframework-webchat-core';
|
|
3
|
+
|
|
4
|
+
type ActivityWithRenderer = {
|
|
5
|
+
activity: WebChatActivity;
|
|
6
|
+
renderActivity: Exclude<ReturnType<ActivityComponentFactory>, false>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type ActivityTree = ActivityWithRenderer[][][];
|
|
10
|
+
type ReadonlyActivityTree = readonly (readonly (readonly ActivityWithRenderer[])[])[];
|
|
11
|
+
|
|
12
|
+
export type { ActivityTree, ActivityWithRenderer, ReadonlyActivityTree };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useCallback, useRef } from 'react';
|
|
2
|
+
import type { WebChatActivity } from 'botframework-webchat-core';
|
|
3
|
+
|
|
4
|
+
import useMemoize from '../../../hooks/internal/useMemoize';
|
|
5
|
+
import type { ActivityWithRenderer } from './types';
|
|
6
|
+
|
|
7
|
+
export default function useActivitiesWithRenderer(
|
|
8
|
+
activities: readonly WebChatActivity[],
|
|
9
|
+
createActivityRenderer
|
|
10
|
+
): readonly ActivityWithRenderer[] {
|
|
11
|
+
const createActivityRendererWithLiteralArgs = useCallback(
|
|
12
|
+
(activity: WebChatActivity, nextVisibleActivity: WebChatActivity) =>
|
|
13
|
+
createActivityRenderer({ activity, nextVisibleActivity }),
|
|
14
|
+
[createActivityRenderer]
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
// Create a memoized context of the createActivityRenderer function.
|
|
18
|
+
// TODO: [P2] Rename useMemoize to useMemoAll
|
|
19
|
+
const entries = useMemoize(
|
|
20
|
+
createActivityRendererWithLiteralArgs,
|
|
21
|
+
createActivityRendererWithLiteralArgsMemoized => {
|
|
22
|
+
// All calls to createActivityRendererWithLiteralArgsMemoized() in this function will be memoized (LRU = 1).
|
|
23
|
+
// In the next render cycle, calls to createActivityRendererWithLiteralArgsMemoized() might return the memoized result instead.
|
|
24
|
+
// This is an improvement to React useMemo(), because it only allows 1 memoization.
|
|
25
|
+
// useMemoize() allows any number of memoization.
|
|
26
|
+
|
|
27
|
+
const activitiesWithRenderer: ActivityWithRenderer[] = [];
|
|
28
|
+
let nextVisibleActivity: WebChatActivity;
|
|
29
|
+
|
|
30
|
+
for (let index = activities.length - 1; index >= 0; index--) {
|
|
31
|
+
const activity = activities[+index];
|
|
32
|
+
const renderActivity = createActivityRendererWithLiteralArgsMemoized(activity, nextVisibleActivity);
|
|
33
|
+
|
|
34
|
+
if (renderActivity) {
|
|
35
|
+
activitiesWithRenderer.splice(0, 0, {
|
|
36
|
+
activity,
|
|
37
|
+
renderActivity
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
nextVisibleActivity = activity;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return Object.freeze(activitiesWithRenderer);
|
|
45
|
+
},
|
|
46
|
+
[activities]
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const prevEntriesRef = useRef<readonly ActivityWithRenderer[]>([]);
|
|
50
|
+
const { current: prevEntries } = prevEntriesRef;
|
|
51
|
+
|
|
52
|
+
if (
|
|
53
|
+
prevEntries.length !== entries.length ||
|
|
54
|
+
prevEntries.some((prevEntry, index) => {
|
|
55
|
+
const entry = entries[+index];
|
|
56
|
+
|
|
57
|
+
return entry.activity !== prevEntry.activity || entry.renderActivity !== prevEntry.renderActivity;
|
|
58
|
+
})
|
|
59
|
+
) {
|
|
60
|
+
prevEntriesRef.current = entries;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return prevEntriesRef.current;
|
|
64
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { hooks } from 'botframework-webchat-api';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import type { WebChatActivity } from 'botframework-webchat-core';
|
|
4
|
+
|
|
5
|
+
import intersectionOf from '../../../Utils/intersectionOf';
|
|
6
|
+
import removeInline from '../../../Utils/removeInline';
|
|
7
|
+
import type { ActivityWithRenderer, ReadonlyActivityTree } from './types';
|
|
8
|
+
|
|
9
|
+
const { useGroupActivities } = hooks;
|
|
10
|
+
|
|
11
|
+
function validateAllEntriesTagged<T>(entries: readonly T[], bins: readonly (readonly T[])[]): boolean {
|
|
12
|
+
return entries.every(entry => bins.some(bin => bin.includes(entry)));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Activity tree is a multidimensional array, while activities is a 1D array.
|
|
16
|
+
// - The first dimension of the array contains activities with same sender;
|
|
17
|
+
// - The second dimension of the array contains activities with same status.
|
|
18
|
+
|
|
19
|
+
// [
|
|
20
|
+
// [
|
|
21
|
+
// // Both messages are from bot and is sent as a batch, we will group them as an array.
|
|
22
|
+
// 'Bot: Hello!'
|
|
23
|
+
// 'Bot: What can I help today?'
|
|
24
|
+
// ],
|
|
25
|
+
// [
|
|
26
|
+
// 'User: What is the weather?'
|
|
27
|
+
// ],
|
|
28
|
+
// [
|
|
29
|
+
// 'Bot: Let me look it up... hold on.'
|
|
30
|
+
// ],
|
|
31
|
+
// [
|
|
32
|
+
// // This message is in a different group because it is more than a few seconds apart from the previous message.
|
|
33
|
+
// 'Bot: Here is the weather forecast.'
|
|
34
|
+
// ]
|
|
35
|
+
// ]
|
|
36
|
+
|
|
37
|
+
function useActivityTreeWithRenderer(entries: readonly ActivityWithRenderer[]): ReadonlyActivityTree {
|
|
38
|
+
const groupActivities = useGroupActivities();
|
|
39
|
+
|
|
40
|
+
// We bin activities in 2 different ways:
|
|
41
|
+
// - `activitiesBySender` is a 2D array containing activities with same sender
|
|
42
|
+
// - `activitiesByStatus` is a 2D array containing activities with same status
|
|
43
|
+
// Both arrays should contains all activities.
|
|
44
|
+
|
|
45
|
+
const { entriesBySender, entriesByStatus } = useMemo<{
|
|
46
|
+
entriesBySender: readonly (readonly ActivityWithRenderer[])[];
|
|
47
|
+
entriesByStatus: readonly (readonly ActivityWithRenderer[])[];
|
|
48
|
+
}>(() => {
|
|
49
|
+
const visibleActivities = entries.map(({ activity }) => activity);
|
|
50
|
+
|
|
51
|
+
const {
|
|
52
|
+
sender: activitiesBySender,
|
|
53
|
+
status: activitiesByStatus
|
|
54
|
+
}: {
|
|
55
|
+
sender: readonly (readonly WebChatActivity[])[];
|
|
56
|
+
status: readonly (readonly WebChatActivity[])[];
|
|
57
|
+
} = groupActivities({
|
|
58
|
+
activities: visibleActivities
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const [entriesBySender, entriesByStatus] = [activitiesBySender, activitiesByStatus].map(bins =>
|
|
62
|
+
bins.map(bin => bin.map(activity => entries.find(entry => entry.activity === activity)))
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (!validateAllEntriesTagged(visibleActivities, activitiesBySender)) {
|
|
66
|
+
console.warn(
|
|
67
|
+
'botframework-webchat: Not every activities are grouped in the "sender" property. Please fix "groupActivitiesMiddleware" and group every activities.'
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!validateAllEntriesTagged(visibleActivities, activitiesByStatus)) {
|
|
72
|
+
console.warn(
|
|
73
|
+
'botframework-webchat: Not every activities are grouped in the "status" property. Please fix "groupActivitiesMiddleware" and group every activities.'
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
entriesBySender,
|
|
79
|
+
entriesByStatus
|
|
80
|
+
};
|
|
81
|
+
}, [entries, groupActivities]);
|
|
82
|
+
|
|
83
|
+
// Create a tree of activities with 2 dimensions: sender, followed by status.
|
|
84
|
+
|
|
85
|
+
const activityTree: ReadonlyActivityTree = useMemo(() => {
|
|
86
|
+
const entriesPendingGrouping = [...entries];
|
|
87
|
+
const activityTree: (readonly (readonly ActivityWithRenderer[])[])[] = [];
|
|
88
|
+
|
|
89
|
+
while (entriesPendingGrouping.length) {
|
|
90
|
+
const entriesWithSameSender = entriesBySender.find(bin => bin.includes(entriesPendingGrouping[0]));
|
|
91
|
+
const senderTree: (readonly ActivityWithRenderer[])[] = [];
|
|
92
|
+
|
|
93
|
+
entriesWithSameSender.forEach(entry => {
|
|
94
|
+
const entriesWithSameStatus = entriesByStatus.find(bin => bin.includes(entry));
|
|
95
|
+
|
|
96
|
+
const entriesWithSameSenderAndStatus = intersectionOf<ActivityWithRenderer>(
|
|
97
|
+
entriesPendingGrouping,
|
|
98
|
+
entriesWithSameSender,
|
|
99
|
+
entriesWithSameStatus
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
if (entriesWithSameSenderAndStatus.length) {
|
|
103
|
+
senderTree.push(Object.freeze(entriesWithSameSenderAndStatus));
|
|
104
|
+
removeInline(entriesPendingGrouping, ...entriesWithSameSenderAndStatus);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
activityTree.push(Object.freeze(senderTree));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Assertion: All entries must be assigned to the activityTree.
|
|
112
|
+
if (
|
|
113
|
+
!entries.every(activity =>
|
|
114
|
+
activityTree.some(activitiesWithSameSender =>
|
|
115
|
+
activitiesWithSameSender.some(activitiesWithSameSenderAndStatus =>
|
|
116
|
+
activitiesWithSameSenderAndStatus.includes(activity)
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
) {
|
|
121
|
+
console.warn('botframework-webchat internal: Not all visible activities are grouped in the activityTree.', {
|
|
122
|
+
entries,
|
|
123
|
+
activityTree
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return Object.freeze(activityTree);
|
|
128
|
+
}, [entriesBySender, entriesByStatus, entries]);
|
|
129
|
+
|
|
130
|
+
return activityTree;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export type { ActivityWithRenderer };
|
|
134
|
+
|
|
135
|
+
export default useActivityTreeWithRenderer;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
import ActivityTreeContext from './Context';
|
|
4
|
+
|
|
5
|
+
import type { ActivityTreeContextType } from './Context';
|
|
6
|
+
|
|
7
|
+
export default function useActivityTreeContext(thrownOnUndefined = true): ActivityTreeContextType {
|
|
8
|
+
const contextValue = useContext(ActivityTreeContext);
|
|
9
|
+
|
|
10
|
+
if (thrownOnUndefined && !contextValue) {
|
|
11
|
+
throw new Error('botframework-webchat internal: This hook can only be used under <ActivityTreeComposer>.');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return contextValue;
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import useActivityTreeContext from './private/useContext';
|
|
2
|
+
|
|
3
|
+
import type { ActivityWithRenderer, ReadonlyActivityTree } from './private/types';
|
|
4
|
+
|
|
5
|
+
export default function useActivityTreeWithRenderer(options?: { flat?: false }): readonly [ReadonlyActivityTree];
|
|
6
|
+
export default function useActivityTreeWithRenderer(options: {
|
|
7
|
+
flat: true;
|
|
8
|
+
}): readonly [readonly ActivityWithRenderer[]];
|
|
9
|
+
|
|
10
|
+
export default function useActivityTreeWithRenderer(options: { flat?: boolean } = {}) {
|
|
11
|
+
const context = useActivityTreeContext();
|
|
12
|
+
|
|
13
|
+
return options?.flat === true
|
|
14
|
+
? context.flattenedActivityTreeWithRendererState
|
|
15
|
+
: context.activityTreeWithRendererState;
|
|
16
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import type { FC, PropsWithChildren } from 'react';
|
|
5
|
+
|
|
6
|
+
import LiveRegionTwinContainer from './private/LiveRegionTwinContainer';
|
|
7
|
+
import LiveRegionTwinContext from './private/Context';
|
|
8
|
+
import useValueRef from '../../hooks/internal/useValueRef';
|
|
9
|
+
|
|
10
|
+
import type { StaticElement, StaticElementEntry } from './private/types';
|
|
11
|
+
|
|
12
|
+
const DEFAULT_ARIA_LIVE = 'polite';
|
|
13
|
+
const DEFAULT_FADE_AFTER = 1000;
|
|
14
|
+
|
|
15
|
+
type LiveRegionTwinComposerProps = PropsWithChildren<{
|
|
16
|
+
/** Optional "aria-label" attribute for the live region twin container. */
|
|
17
|
+
'aria-label'?: string;
|
|
18
|
+
|
|
19
|
+
/** "aria-live" attribute for the live region twin container, defaults to `'polite'`. */
|
|
20
|
+
'aria-live'?: 'assertive' | 'polite';
|
|
21
|
+
|
|
22
|
+
/** Optional "aria-roledescription" attribute for the live region twin container. */
|
|
23
|
+
'aria-roledescription'?: string;
|
|
24
|
+
|
|
25
|
+
/** Optional "className" attribute for the live region twin container. */
|
|
26
|
+
className?: string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Static elements will fade out after this timeout value specified in milliseconds, defaults to `1000`.
|
|
30
|
+
*
|
|
31
|
+
* When lowering this value, make sure screen reader can continue to pick up new static elements before fading out.
|
|
32
|
+
*
|
|
33
|
+
* If this prop is updated, it will be reflected in next queueing elements.
|
|
34
|
+
*/
|
|
35
|
+
fadeAfter?: number;
|
|
36
|
+
|
|
37
|
+
/** Optional "role" attribute for the live region twin container. */
|
|
38
|
+
role?: string;
|
|
39
|
+
|
|
40
|
+
/** Optional "className" attribute for static text element. */
|
|
41
|
+
textElementClassName?: string;
|
|
42
|
+
}>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Live region twin is an UI component for queueing texts or elements to the screen reader using
|
|
46
|
+
* a container element with `aria-live` attribute set.
|
|
47
|
+
*
|
|
48
|
+
* After the text is rendered and queued, it will be removed to reduce burden on the DOM tree.
|
|
49
|
+
* Currently, we assume the assistive technologies should pick up the text within 1 second of rendering.
|
|
50
|
+
* This value is configurable.
|
|
51
|
+
*
|
|
52
|
+
* By default, the live region is visible. If is is not desirable, the caller can use `className` prop to
|
|
53
|
+
* hide its visuals.
|
|
54
|
+
*/
|
|
55
|
+
const LiveRegionTwinComposer: FC<LiveRegionTwinComposerProps> = ({
|
|
56
|
+
'aria-label': ariaLabel,
|
|
57
|
+
'aria-live': ariaLive = DEFAULT_ARIA_LIVE,
|
|
58
|
+
'aria-roledescription': ariaRoleDescription,
|
|
59
|
+
children,
|
|
60
|
+
className,
|
|
61
|
+
fadeAfter = DEFAULT_FADE_AFTER,
|
|
62
|
+
role,
|
|
63
|
+
textElementClassName
|
|
64
|
+
}) => {
|
|
65
|
+
const [staticElementEntries, setStaticElementEntries] = useState<StaticElementEntry[]>([]);
|
|
66
|
+
const fadeAfterRef = useValueRef(fadeAfter);
|
|
67
|
+
const markAllAsRenderedTimeoutIdRef = useRef<any>();
|
|
68
|
+
const nextKeyRef = useRef<number>(1);
|
|
69
|
+
|
|
70
|
+
const staticElementEntriesRef = useValueRef(staticElementEntries);
|
|
71
|
+
|
|
72
|
+
// This function is called by an effect hook `useMarkAllAsRenderedEffect`, it must be designed with converging in mind.
|
|
73
|
+
// To prevent infinite render loop, after multiple calls to this function, it should eventually no-op.
|
|
74
|
+
const markAllAsRendered = useCallback<() => void>(() => {
|
|
75
|
+
if (!staticElementEntriesRef.current.length) {
|
|
76
|
+
// Nothing to remove.
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// When removing each element one-by-one based on an individual timeout, Narrator would narrate them twice occasionally.
|
|
81
|
+
// Possibly it think some elements that is not removed during the current cycle, are new elements and queued them twice.
|
|
82
|
+
// Thus, we are removing all at once to prevent bugs in Narrator.
|
|
83
|
+
markAllAsRenderedTimeoutIdRef.current && clearTimeout(markAllAsRenderedTimeoutIdRef.current);
|
|
84
|
+
|
|
85
|
+
markAllAsRenderedTimeoutIdRef.current = setTimeout(() => {
|
|
86
|
+
// We are playing safe by using value ref to check its length here.
|
|
87
|
+
// If we are certain that `setStaticElements(emptyArray => emptyArray)` is a no-op,
|
|
88
|
+
// we could replace it with just the setter function.
|
|
89
|
+
staticElementEntriesRef.current.length && setStaticElementEntries([]);
|
|
90
|
+
}, fadeAfterRef.current);
|
|
91
|
+
}, [fadeAfterRef, markAllAsRenderedTimeoutIdRef, setStaticElementEntries, staticElementEntriesRef]);
|
|
92
|
+
|
|
93
|
+
// When this component is unmounting, make sure all future `setTimeout` are cleared and should not be fired.
|
|
94
|
+
useEffect(
|
|
95
|
+
() => () => markAllAsRenderedTimeoutIdRef.current && clearTimeout(markAllAsRenderedTimeoutIdRef.current),
|
|
96
|
+
[markAllAsRenderedTimeoutIdRef]
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const queueStaticElement = useCallback<(staticElement: StaticElement) => void>(
|
|
100
|
+
(element: StaticElement): void => {
|
|
101
|
+
const key = nextKeyRef.current;
|
|
102
|
+
|
|
103
|
+
nextKeyRef.current = nextKeyRef.current + 1;
|
|
104
|
+
|
|
105
|
+
setStaticElementEntries(entries => [...entries, { element, key }]);
|
|
106
|
+
},
|
|
107
|
+
[nextKeyRef, setStaticElementEntries]
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const staticElementEntriesState = useMemo<readonly [readonly StaticElementEntry[]]>(
|
|
111
|
+
() => Object.freeze([Object.freeze(staticElementEntries)]) as readonly [readonly StaticElementEntry[]],
|
|
112
|
+
[staticElementEntries]
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const context = useMemo(
|
|
116
|
+
() => ({
|
|
117
|
+
markAllAsRendered,
|
|
118
|
+
queueStaticElement,
|
|
119
|
+
staticElementEntriesState
|
|
120
|
+
}),
|
|
121
|
+
[markAllAsRendered, queueStaticElement, staticElementEntriesState]
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<LiveRegionTwinContext.Provider value={context}>
|
|
126
|
+
<LiveRegionTwinContainer
|
|
127
|
+
aria-label={ariaLabel}
|
|
128
|
+
aria-live={ariaLive}
|
|
129
|
+
aria-roledescription={ariaRoleDescription}
|
|
130
|
+
className={className}
|
|
131
|
+
role={role}
|
|
132
|
+
textElementClassName={textElementClassName}
|
|
133
|
+
/>
|
|
134
|
+
{children}
|
|
135
|
+
</LiveRegionTwinContext.Provider>
|
|
136
|
+
);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
LiveRegionTwinComposer.defaultProps = {
|
|
140
|
+
'aria-label': undefined,
|
|
141
|
+
'aria-live': DEFAULT_ARIA_LIVE,
|
|
142
|
+
'aria-roledescription': undefined,
|
|
143
|
+
children: undefined,
|
|
144
|
+
className: undefined,
|
|
145
|
+
fadeAfter: DEFAULT_FADE_AFTER,
|
|
146
|
+
role: undefined,
|
|
147
|
+
textElementClassName: undefined
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
LiveRegionTwinComposer.propTypes = {
|
|
151
|
+
'aria-label': PropTypes.string,
|
|
152
|
+
'aria-live': PropTypes.oneOf(['assertive', 'polite']),
|
|
153
|
+
'aria-roledescription': PropTypes.string,
|
|
154
|
+
children: PropTypes.any,
|
|
155
|
+
className: PropTypes.string,
|
|
156
|
+
fadeAfter: PropTypes.number,
|
|
157
|
+
role: PropTypes.string,
|
|
158
|
+
textElementClassName: PropTypes.string
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export default LiveRegionTwinComposer;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { StaticElement, StaticElementEntry } from './types';
|
|
4
|
+
|
|
5
|
+
type LiveRegionTwinContextType = {
|
|
6
|
+
markAllAsRendered: () => void;
|
|
7
|
+
queueStaticElement: (element: StaticElement) => void;
|
|
8
|
+
staticElementEntriesState: readonly [readonly StaticElementEntry[]];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const LiveRegionTwinContext = createContext<LiveRegionTwinContextType>(undefined);
|
|
12
|
+
|
|
13
|
+
export default LiveRegionTwinContext;
|
|
14
|
+
|
|
15
|
+
export type { LiveRegionTwinContextType };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import React, { Fragment } from 'react';
|
|
3
|
+
|
|
4
|
+
import type { VFC } from 'react';
|
|
5
|
+
|
|
6
|
+
import useMarkAllAsRenderedEffect from './useMarkAllAsRenderedEffect';
|
|
7
|
+
import useStaticElementEntries from './useStaticElementEntries';
|
|
8
|
+
|
|
9
|
+
type LiveRegionTwinContainerProps = {
|
|
10
|
+
'aria-label'?: string;
|
|
11
|
+
'aria-live': 'assertive' | 'polite';
|
|
12
|
+
'aria-roledescription'?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
role?: string;
|
|
15
|
+
textElementClassName?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// This container is marked as private because we assume there is only one instance under the <LiveRegionTwinContext>.
|
|
19
|
+
const LiveRegionTwinContainer: VFC<LiveRegionTwinContainerProps> = ({
|
|
20
|
+
'aria-label': ariaLabel,
|
|
21
|
+
'aria-live': ariaLive,
|
|
22
|
+
'aria-roledescription': ariaRoleDescription,
|
|
23
|
+
className,
|
|
24
|
+
role,
|
|
25
|
+
textElementClassName
|
|
26
|
+
}) => {
|
|
27
|
+
const [staticElementEntries] = useStaticElementEntries();
|
|
28
|
+
|
|
29
|
+
// We assume there is only one instance under the <LiveRegionTwinContext>.
|
|
30
|
+
// The assumption made us safe to mark everything is rendered.
|
|
31
|
+
// In contrary, if we have 0-to-many at different time, we may falsely mark something as rendered.
|
|
32
|
+
useMarkAllAsRenderedEffect();
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div
|
|
36
|
+
aria-label={ariaLabel}
|
|
37
|
+
aria-live={ariaLive}
|
|
38
|
+
aria-roledescription={ariaRoleDescription}
|
|
39
|
+
className={className}
|
|
40
|
+
role={role}
|
|
41
|
+
>
|
|
42
|
+
{staticElementEntries.map(({ element, key }) => {
|
|
43
|
+
if (typeof element === 'string') {
|
|
44
|
+
const id = `webchat__live-region-twin__text-element-${key}`;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div aria-atomic={true} aria-labelledby={id} className={textElementClassName} key={key}>
|
|
48
|
+
{/* "aria-labelledby" requires the use of "id" attribute. */}
|
|
49
|
+
{/* eslint-disable-next-line react/forbid-dom-props */}
|
|
50
|
+
<p id={id}>{element}</p>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return <Fragment key={key}>{element}</Fragment>;
|
|
56
|
+
})}
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
LiveRegionTwinContainer.defaultProps = {
|
|
62
|
+
'aria-label': undefined,
|
|
63
|
+
'aria-roledescription': undefined,
|
|
64
|
+
className: undefined,
|
|
65
|
+
role: undefined,
|
|
66
|
+
textElementClassName: undefined
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
LiveRegionTwinContainer.propTypes = {
|
|
70
|
+
'aria-label': PropTypes.string,
|
|
71
|
+
// PropTypes.oneOf() returns type of `string`, but not `'assertive' | 'polite'`.
|
|
72
|
+
// @ts-ignore
|
|
73
|
+
'aria-live': PropTypes.oneOf(['assertive', 'polite']).isRequired,
|
|
74
|
+
'aria-roledescription': PropTypes.string,
|
|
75
|
+
className: PropTypes.string,
|
|
76
|
+
role: PropTypes.string,
|
|
77
|
+
textElementClassName: PropTypes.string
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export default LiveRegionTwinContainer;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
import LiveRegionTwinContext from './Context';
|
|
4
|
+
|
|
5
|
+
import type { LiveRegionTwinContextType } from './Context';
|
|
6
|
+
|
|
7
|
+
export default function useLiveRegionTwinContext(thrownOnUndefined = true): LiveRegionTwinContextType {
|
|
8
|
+
const contextValue = useContext(LiveRegionTwinContext);
|
|
9
|
+
|
|
10
|
+
if (thrownOnUndefined && !contextValue) {
|
|
11
|
+
throw new Error('botframework-webchat internal: This hook can only be used under <LiveRegionComposer>.');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return contextValue;
|
|
15
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
import useLiveRegionTwinContext from './useContext';
|
|
4
|
+
|
|
5
|
+
// After calling `markAllAsRendered`, it will cause a re-render.
|
|
6
|
+
// Since `markAllAsRendered` is a converging function, it will eventually stop re-rendering.
|
|
7
|
+
// We call this hook `useXXXEffect` for us to abstract the business logic here.
|
|
8
|
+
export default function useMarkAllAsRenderedEffect(): void {
|
|
9
|
+
const { markAllAsRendered } = useLiveRegionTwinContext();
|
|
10
|
+
|
|
11
|
+
// We did not set the `deps` argument as we want to run this function on every render.
|
|
12
|
+
useEffect(markAllAsRendered);
|
|
13
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import useLiveRegionTwinContext from './useContext';
|
|
2
|
+
|
|
3
|
+
import type { StaticElementEntry } from './types';
|
|
4
|
+
|
|
5
|
+
export default function useStaticElementEntries(): readonly [readonly StaticElementEntry[]] {
|
|
6
|
+
return useLiveRegionTwinContext().staticElementEntriesState;
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import useLiveRegionContext from './private/useContext';
|
|
2
|
+
|
|
3
|
+
import type { StaticElement } from './private/types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Queues a static element to the live region.
|
|
7
|
+
*
|
|
8
|
+
* After the element is queued, screen reader will eventually narrate it and it cannot be changed.
|
|
9
|
+
*/
|
|
10
|
+
export default function useQueueStaticElement(): (element: StaticElement) => void {
|
|
11
|
+
return useLiveRegionContext().queueStaticElement;
|
|
12
|
+
}
|