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,196 @@
|
|
|
1
|
+
import { hooks } from 'botframework-webchat-api';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import random from 'math-random';
|
|
5
|
+
import React, { useEffect, useMemo, useRef } from 'react';
|
|
6
|
+
import type { FC, RefObject, VFC } from 'react';
|
|
7
|
+
import type { WebChatActivity } from 'botframework-webchat-core';
|
|
8
|
+
|
|
9
|
+
import LiveRegionTwinComposer from '../providers/LiveRegionTwin/LiveRegionTwinComposer';
|
|
10
|
+
import ScreenReaderActivity from '../ScreenReaderActivity';
|
|
11
|
+
import tabbableElements from '../Utils/tabbableElements';
|
|
12
|
+
import useActivityTreeWithRenderer from '../providers/ActivityTree/useActivityTreeWithRenderer';
|
|
13
|
+
import useQueueStaticElement from '../providers/LiveRegionTwin/useQueueStaticElement';
|
|
14
|
+
import useStyleToEmotionObject from '../hooks/internal/useStyleToEmotionObject';
|
|
15
|
+
import useTypistNames from './useTypistNames';
|
|
16
|
+
|
|
17
|
+
import type { ActivityElementMap } from './types';
|
|
18
|
+
|
|
19
|
+
const { useGetKeyByActivity, useLocalizer, useStyleOptions } = hooks;
|
|
20
|
+
|
|
21
|
+
const ROOT_STYLE = {
|
|
22
|
+
'&.webchat__live-region-transcript': {
|
|
23
|
+
'& .webchat__live-region-transcript__interactive-note, & .webchat__live-region-transcript__text-element': {
|
|
24
|
+
color: 'transparent',
|
|
25
|
+
height: 1,
|
|
26
|
+
overflow: 'hidden',
|
|
27
|
+
position: 'absolute',
|
|
28
|
+
top: 0,
|
|
29
|
+
whiteSpace: 'nowrap',
|
|
30
|
+
width: 1
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Checks if the rendering activity is presentational or not. Returns `true` if presentational, otherwise, `false`.
|
|
37
|
+
*
|
|
38
|
+
* Presentational activity, will be rendered visually but not going through screen reader.
|
|
39
|
+
*/
|
|
40
|
+
function isPresentational(activity: WebChatActivity): boolean {
|
|
41
|
+
if (activity.type !== 'message') {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const { channelData } = activity;
|
|
46
|
+
|
|
47
|
+
// "Fallback text" includes both message text and narratives for attachments.
|
|
48
|
+
// Emptying out "fallback text" essentially mute for both message and attachments.
|
|
49
|
+
const fallbackText = channelData?.['webchat:fallback-text'];
|
|
50
|
+
|
|
51
|
+
if (typeof fallbackText === 'string') {
|
|
52
|
+
return !fallbackText;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// If there are "displayText" (MessageBack), "text", or any attachments, there are something to narrate.
|
|
56
|
+
return !(channelData?.messageBack?.displayText || activity.text || activity.attachments?.length);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type RenderingActivities = Map<string, WebChatActivity>;
|
|
60
|
+
|
|
61
|
+
type LiveRegionTranscriptCoreProps = {
|
|
62
|
+
activityElementMapRef: RefObject<ActivityElementMap>;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const LiveRegionTranscriptCore: FC<LiveRegionTranscriptCoreProps> = ({ activityElementMapRef }) => {
|
|
66
|
+
const [flattenedActivityTree] = useActivityTreeWithRenderer({ flat: true });
|
|
67
|
+
const [typistNames] = useTypistNames();
|
|
68
|
+
const getKeyByActivity = useGetKeyByActivity();
|
|
69
|
+
const localize = useLocalizer();
|
|
70
|
+
const queueStaticElement = useQueueStaticElement();
|
|
71
|
+
|
|
72
|
+
const liveRegionInteractiveLabelAlt = localize('TRANSCRIPT_LIVE_REGION_INTERACTIVE_LABEL_ALT');
|
|
73
|
+
const liveRegionInteractiveWithLinkLabelAlt = localize('TRANSCRIPT_LIVE_REGION_INTERACTIVE_WITH_LINKS_LABEL_ALT');
|
|
74
|
+
const typingIndicator =
|
|
75
|
+
!!typistNames.length &&
|
|
76
|
+
localize(
|
|
77
|
+
typistNames.length > 1 ? 'TYPING_INDICATOR_MULTIPLE_TEXT' : 'TYPING_INDICATOR_SINGLE_TEXT',
|
|
78
|
+
typistNames[0]
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const renderingActivities = useMemo<Readonly<RenderingActivities>>(
|
|
82
|
+
() =>
|
|
83
|
+
Object.freeze(
|
|
84
|
+
flattenedActivityTree.reduce<RenderingActivities>(
|
|
85
|
+
(intermediate, { activity }) => intermediate.set(getKeyByActivity(activity), activity),
|
|
86
|
+
new Map<string, WebChatActivity>()
|
|
87
|
+
)
|
|
88
|
+
),
|
|
89
|
+
[flattenedActivityTree, getKeyByActivity]
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const prevRenderingActivitiesRef = useRef<Readonly<RenderingActivities>>();
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
const { current: prevRenderingActivities } = prevRenderingActivitiesRef;
|
|
96
|
+
const appendedActivities: { activity: WebChatActivity; key: string }[] = [];
|
|
97
|
+
|
|
98
|
+
// Bottom-up, find activities which are recently appended (i.e. new activity will have a new key).
|
|
99
|
+
// We only consider new activities added to the bottom of the chat history.
|
|
100
|
+
// Based on how `aria-relevant="additions"` works, activities that are updated, deleted, or reordered, should be ignored.
|
|
101
|
+
for (const [key, activity] of Array.from(renderingActivities.entries()).reverse()) {
|
|
102
|
+
if (prevRenderingActivities?.has(key)) {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
appendedActivities.unshift({ activity, key });
|
|
107
|
+
|
|
108
|
+
isPresentational(activity) || queueStaticElement(<ScreenReaderActivity activity={activity} />);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const hasNewLink = appendedActivities.some(({ key }) => activityElementMapRef.current.get(key)?.querySelector('a'));
|
|
112
|
+
|
|
113
|
+
const hasNewWidget = appendedActivities.some(
|
|
114
|
+
({ key }) =>
|
|
115
|
+
!!tabbableElements(
|
|
116
|
+
activityElementMapRef.current.get(key)?.querySelector('.webchat__basic-transcript__activity-body')
|
|
117
|
+
).length
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
if (hasNewLink || hasNewWidget) {
|
|
121
|
+
// eslint-disable-next-line no-magic-numbers
|
|
122
|
+
const labelId = `webchat__live-region-transcript__interactive-note--${random().toString(36).substr(2, 5)}`;
|
|
123
|
+
|
|
124
|
+
queueStaticElement(
|
|
125
|
+
// Inside ARIA live region:
|
|
126
|
+
// - Edge + Narrator:
|
|
127
|
+
// - It read if `aria-labelledby` or `aria-label` is set;
|
|
128
|
+
// - It read nothing if `aria-labelledby` or `aria-label` are not set (in this case, it read "note").
|
|
129
|
+
// - Safari + VoiceOver and Chrome + NVDA:
|
|
130
|
+
// - They read its content and ignore `aria-labelledby` or `aria-label`
|
|
131
|
+
// - They will not read if it is simply <div aria-label="Something" /> without content (self-closing tag).
|
|
132
|
+
// For best compatibility, we need both `aria-labelledby` and contented <div>.
|
|
133
|
+
<div
|
|
134
|
+
aria-atomic="true"
|
|
135
|
+
aria-labelledby={labelId}
|
|
136
|
+
className="webchat__live-region-transcript__interactive-note"
|
|
137
|
+
role="note"
|
|
138
|
+
>
|
|
139
|
+
{/* "id" is required for "aria-activedescendant" */}
|
|
140
|
+
{/* eslint-disable-next-line react/forbid-dom-props */}
|
|
141
|
+
<span id={labelId}>{hasNewLink ? liveRegionInteractiveWithLinkLabelAlt : liveRegionInteractiveLabelAlt}</span>
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
prevRenderingActivitiesRef.current = renderingActivities;
|
|
147
|
+
}, [
|
|
148
|
+
activityElementMapRef,
|
|
149
|
+
getKeyByActivity,
|
|
150
|
+
liveRegionInteractiveLabelAlt,
|
|
151
|
+
liveRegionInteractiveWithLinkLabelAlt,
|
|
152
|
+
prevRenderingActivitiesRef,
|
|
153
|
+
queueStaticElement,
|
|
154
|
+
renderingActivities
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
typingIndicator && queueStaticElement(typingIndicator);
|
|
159
|
+
}, [queueStaticElement, typingIndicator]);
|
|
160
|
+
|
|
161
|
+
return null;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
type LiveRegionTranscriptProps = {
|
|
165
|
+
activityElementMapRef: RefObject<ActivityElementMap>;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const LiveRegionTranscript: VFC<LiveRegionTranscriptProps> = ({ activityElementMapRef }) => {
|
|
169
|
+
const [{ internalLiveRegionFadeAfter }] = useStyleOptions();
|
|
170
|
+
const localize = useLocalizer();
|
|
171
|
+
const rootClassName = useStyleToEmotionObject()(ROOT_STYLE) + '';
|
|
172
|
+
|
|
173
|
+
const transcriptRoleDescription = localize('TRANSCRIPT_ARIA_ROLE_ALT');
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<LiveRegionTwinComposer
|
|
177
|
+
aria-roledescription={transcriptRoleDescription}
|
|
178
|
+
className={classNames('webchat__live-region-transcript', rootClassName)}
|
|
179
|
+
fadeAfter={internalLiveRegionFadeAfter}
|
|
180
|
+
role="log"
|
|
181
|
+
textElementClassName="webchat__live-region-transcript__text-element"
|
|
182
|
+
>
|
|
183
|
+
<LiveRegionTranscriptCore activityElementMapRef={activityElementMapRef} />
|
|
184
|
+
</LiveRegionTwinComposer>
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
LiveRegionTranscript.propTypes = {
|
|
189
|
+
// PropTypes cannot be fully expressed in TypeScript.
|
|
190
|
+
// @ts-ignore
|
|
191
|
+
activityElementMapRef: PropTypes.shape({
|
|
192
|
+
current: PropTypes.instanceOf(Map)
|
|
193
|
+
}).isRequired
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export default LiveRegionTranscript;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type ActivityElementMap = Map<string, HTMLElement>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { hooks } from 'botframework-webchat-api';
|
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import type { RefObject } from 'react';
|
|
4
|
+
import type { WebChatActivity } from 'botframework-webchat-core';
|
|
5
|
+
|
|
6
|
+
import activityAltText from '../Utils/activityAltText';
|
|
7
|
+
import tabbableElements from '../Utils/tabbableElements';
|
|
8
|
+
import useRenderMarkdownAsHTML from '../hooks/useRenderMarkdownAsHTML';
|
|
9
|
+
import useValueRef from '../hooks/internal/useValueRef';
|
|
10
|
+
|
|
11
|
+
enum InteractiveType {
|
|
12
|
+
LINK,
|
|
13
|
+
WIDGET
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { useAvatarForBot, useLocalizer } = hooks;
|
|
17
|
+
|
|
18
|
+
const ACTIVITY_NUM_ATTACHMENTS_ALT_IDS = {
|
|
19
|
+
few: 'ACTIVITY_NUM_ATTACHMENTS_FEW_ALT',
|
|
20
|
+
many: 'ACTIVITY_NUM_ATTACHMENTS_MANY_ALT',
|
|
21
|
+
one: 'ACTIVITY_NUM_ATTACHMENTS_ONE_ALT',
|
|
22
|
+
other: 'ACTIVITY_NUM_ATTACHMENTS_OTHER_ALT',
|
|
23
|
+
two: 'ACTIVITY_NUM_ATTACHMENTS_TWO_ALT'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default function useActivityAccessibleName(activity: WebChatActivity, bodyRef: RefObject<HTMLElement>) {
|
|
27
|
+
const [{ initials: botInitials }] = useAvatarForBot();
|
|
28
|
+
const [interactiveType, setInteractiveType] = useState<InteractiveType | false>(false);
|
|
29
|
+
const fromSelf = activity.from?.role === 'user';
|
|
30
|
+
const localize = useLocalizer();
|
|
31
|
+
const localizeWithPlural = useLocalizer({ plural: true });
|
|
32
|
+
const numAttachments = activity.type === 'message' ? activity.attachments?.length || 0 : 0;
|
|
33
|
+
const renderMarkdownAsHTML = useRenderMarkdownAsHTML();
|
|
34
|
+
|
|
35
|
+
const activityInteractiveAlt = localize('ACTIVITY_INTERACTIVE_LABEL_ALT'); // "Click to interact."
|
|
36
|
+
const activityInteractiveWithLinkAlt = localize('ACTIVITY_INTERACTIVE_WITH_LINKS_LABEL_ALT'); // "Click to interact."
|
|
37
|
+
const greetingAlt = useMemo(
|
|
38
|
+
() =>
|
|
39
|
+
(fromSelf ? localize('ACTIVITY_YOU_SAID_ALT') : localize('ACTIVITY_BOT_SAID_ALT', botInitials || '')).replace(
|
|
40
|
+
/\s{2,}/gu,
|
|
41
|
+
' '
|
|
42
|
+
),
|
|
43
|
+
[botInitials, fromSelf, localize]
|
|
44
|
+
);
|
|
45
|
+
const interactiveTypeRef = useValueRef(interactiveType);
|
|
46
|
+
const messageTextAlt = useMemo(
|
|
47
|
+
() => activityAltText(activity, renderMarkdownAsHTML),
|
|
48
|
+
[activity, renderMarkdownAsHTML]
|
|
49
|
+
);
|
|
50
|
+
const numAttachmentsAlt = useMemo(
|
|
51
|
+
() => (numAttachments ? localizeWithPlural(ACTIVITY_NUM_ATTACHMENTS_ALT_IDS, numAttachments) : ''),
|
|
52
|
+
[localizeWithPlural, numAttachments]
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const accessibleName = useMemo(
|
|
56
|
+
// We are concatenating in a single string for Safari. If we split it up, Safari will only narrate the first section.
|
|
57
|
+
() =>
|
|
58
|
+
`${greetingAlt} ${messageTextAlt} ${numAttachmentsAlt} ${
|
|
59
|
+
interactiveType === InteractiveType.LINK
|
|
60
|
+
? activityInteractiveWithLinkAlt
|
|
61
|
+
: interactiveType === InteractiveType.WIDGET
|
|
62
|
+
? activityInteractiveAlt
|
|
63
|
+
: ''
|
|
64
|
+
}`,
|
|
65
|
+
[
|
|
66
|
+
activityInteractiveAlt,
|
|
67
|
+
activityInteractiveWithLinkAlt,
|
|
68
|
+
greetingAlt,
|
|
69
|
+
interactiveType,
|
|
70
|
+
messageTextAlt,
|
|
71
|
+
numAttachmentsAlt
|
|
72
|
+
]
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
const hasLinks = !!bodyRef.current?.querySelector('a');
|
|
77
|
+
const hasWidgets = !!tabbableElements(bodyRef.current).length;
|
|
78
|
+
|
|
79
|
+
interactiveTypeRef.current !== hasWidgets &&
|
|
80
|
+
setInteractiveType(hasLinks ? InteractiveType.LINK : hasWidgets ? InteractiveType.WIDGET : false);
|
|
81
|
+
}, [bodyRef, interactiveTypeRef, setInteractiveType]);
|
|
82
|
+
|
|
83
|
+
return [accessibleName];
|
|
84
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { hooks } from 'botframework-webchat-api';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
|
|
4
|
+
const { useActiveTyping } = hooks;
|
|
5
|
+
|
|
6
|
+
function arrayEquals<T>(x: readonly T[], y: readonly T[]): boolean {
|
|
7
|
+
return x.length === y.length && x.every((value, index) => y[+index] === value);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Gets names of users who are actively typing, sorted by the time they started typing. */
|
|
11
|
+
export default function useTypistNames(): readonly [readonly string[]] {
|
|
12
|
+
const [activeTyping] = useActiveTyping();
|
|
13
|
+
const prevTypistNamesStateRef = useRef<readonly [readonly string[]]>(
|
|
14
|
+
Object.freeze([Object.freeze([] as string[])]) as [readonly string[]]
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const activeTypingFromOthersValues = Object.values(activeTyping).filter(({ role }) => role !== 'user');
|
|
18
|
+
|
|
19
|
+
// Sort the list by the first typist.
|
|
20
|
+
const sortedActiveTypingFromOthersValues = activeTypingFromOthersValues.sort(({ at: x }, { at: y }) => x - y);
|
|
21
|
+
|
|
22
|
+
const typistNamesState: readonly [readonly string[]] = Object.freeze([
|
|
23
|
+
Object.freeze(sortedActiveTypingFromOthersValues.map(({ name }) => name))
|
|
24
|
+
]) as readonly [readonly string[]];
|
|
25
|
+
|
|
26
|
+
const { current: prevTypistNamesState } = prevTypistNamesStateRef;
|
|
27
|
+
|
|
28
|
+
const nextTypistNamesState = arrayEquals(typistNamesState[0], prevTypistNamesState[0])
|
|
29
|
+
? prevTypistNamesState
|
|
30
|
+
: typistNamesState;
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
prevTypistNamesStateRef.current = nextTypistNamesState;
|
|
34
|
+
}, [prevTypistNamesStateRef, nextTypistNamesState]);
|
|
35
|
+
|
|
36
|
+
return nextTypistNamesState;
|
|
37
|
+
}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
/* eslint no-magic-numbers: ["error", { "ignore": [-1] }] */
|
|
2
2
|
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
|
-
import React, { forwardRef, useRef } from 'react';
|
|
4
|
+
import React, { forwardRef, MouseEventHandler, ReactNode, useRef } from 'react';
|
|
5
5
|
|
|
6
6
|
const PREVENT_DEFAULT_HANDLER = event => event.preventDefault();
|
|
7
7
|
|
|
8
|
+
type AccessibleButtonProps = {
|
|
9
|
+
'aria-hidden'?: boolean;
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
onClick?: MouseEventHandler<HTMLButtonElement>;
|
|
13
|
+
tabIndex?: number;
|
|
14
|
+
type: 'button';
|
|
15
|
+
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
|
|
16
|
+
|
|
8
17
|
// Differences between <button> and <AccessibleButton>:
|
|
9
18
|
// - Disable behavior
|
|
10
19
|
// - When the widget is disabled
|
|
@@ -23,8 +32,8 @@ const PREVENT_DEFAULT_HANDLER = event => event.preventDefault();
|
|
|
23
32
|
// - aria-disabled="true" is the source of truth
|
|
24
33
|
// - If the widget is contained by a <form>, the developer need to filter out some `onSubmit` event caused by this widget
|
|
25
34
|
|
|
26
|
-
const AccessibleButton = forwardRef(
|
|
27
|
-
({ 'aria-hidden': ariaHidden, disabled, onClick, tabIndex, ...props }, forwardedRef) => {
|
|
35
|
+
const AccessibleButton = forwardRef<HTMLButtonElement, AccessibleButtonProps>(
|
|
36
|
+
({ 'aria-hidden': ariaHidden, children, disabled, onClick, tabIndex, ...props }, forwardedRef) => {
|
|
28
37
|
const targetRef = useRef();
|
|
29
38
|
|
|
30
39
|
const ref = forwardedRef || targetRef;
|
|
@@ -38,13 +47,16 @@ const AccessibleButton = forwardRef(
|
|
|
38
47
|
tabIndex={disabled ? -1 : tabIndex}
|
|
39
48
|
{...props}
|
|
40
49
|
type="button"
|
|
41
|
-
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
</button>
|
|
42
53
|
);
|
|
43
54
|
}
|
|
44
55
|
);
|
|
45
56
|
|
|
46
57
|
AccessibleButton.defaultProps = {
|
|
47
58
|
'aria-hidden': undefined,
|
|
59
|
+
children: undefined,
|
|
48
60
|
disabled: undefined,
|
|
49
61
|
onClick: undefined,
|
|
50
62
|
tabIndex: undefined
|
|
@@ -54,9 +66,12 @@ AccessibleButton.displayName = 'AccessibleButton';
|
|
|
54
66
|
|
|
55
67
|
AccessibleButton.propTypes = {
|
|
56
68
|
'aria-hidden': PropTypes.bool,
|
|
69
|
+
children: PropTypes.any,
|
|
57
70
|
disabled: PropTypes.bool,
|
|
58
71
|
onClick: PropTypes.func,
|
|
59
72
|
tabIndex: PropTypes.number,
|
|
73
|
+
// TypeScript class is not mappable to PropTypes.oneOf(['button'])
|
|
74
|
+
// @ts-ignore
|
|
60
75
|
type: PropTypes.oneOf(['button']).isRequired
|
|
61
76
|
};
|
|
62
77
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
2
|
import React, { useCallback } from 'react';
|
|
3
3
|
|
|
4
|
+
import type { FC, MutableRefObject } from 'react';
|
|
5
|
+
|
|
4
6
|
// This is an element, when focused, will send the focus to the ref specified in "redirectRef".
|
|
5
7
|
// Although the focus is being redirected, browser will scroll this redirector element into view.
|
|
6
8
|
|
|
@@ -10,28 +12,39 @@ import React, { useCallback } from 'react';
|
|
|
10
12
|
// When this focus redirector is put inside a scrollable container, you may want to resize or reposition
|
|
11
13
|
// it to prevent unintentional scroll done by the browser default behavior.
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
type FocusRedirectorProps = {
|
|
16
|
+
className?: string;
|
|
17
|
+
onFocus?: () => void;
|
|
18
|
+
redirectRef?: MutableRefObject<HTMLElement>;
|
|
19
|
+
};
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
const FocusRedirector: FC<FocusRedirectorProps> = ({ className, onFocus, redirectRef }) => {
|
|
22
|
+
const handleFocus = useCallback(() => {
|
|
23
|
+
redirectRef?.current?.focus();
|
|
18
24
|
onFocus && onFocus();
|
|
19
25
|
}, [onFocus, redirectRef]);
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
// For NVDA, we should set aria-hidden="true".
|
|
28
|
+
// When using NVDA in browse mode, press up/down arrow keys will focus on this redirector.
|
|
29
|
+
// This redirector is designed to capture TAB only and should not react on browse mode.
|
|
30
|
+
// However, reacting with browse mode is currently okay. Just better to leave it alone.
|
|
31
|
+
return <div aria-hidden="true" className={className} onFocus={handleFocus} tabIndex={0} />;
|
|
22
32
|
};
|
|
23
33
|
|
|
24
34
|
FocusRedirector.defaultProps = {
|
|
25
35
|
className: undefined,
|
|
26
|
-
onFocus: undefined
|
|
36
|
+
onFocus: undefined,
|
|
37
|
+
redirectRef: undefined
|
|
27
38
|
};
|
|
28
39
|
|
|
29
40
|
FocusRedirector.propTypes = {
|
|
30
41
|
className: PropTypes.string,
|
|
31
42
|
onFocus: PropTypes.func,
|
|
43
|
+
// PropTypes is not fully compatible with TypeScript.
|
|
44
|
+
// @ts-ignore
|
|
32
45
|
redirectRef: PropTypes.shape({
|
|
33
|
-
current: PropTypes.
|
|
34
|
-
})
|
|
46
|
+
current: PropTypes.instanceOf(HTMLElement)
|
|
47
|
+
})
|
|
35
48
|
};
|
|
36
49
|
|
|
37
50
|
export default FocusRedirector;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint react/no-danger: "off" */
|
|
2
2
|
|
|
3
3
|
import { hooks } from 'botframework-webchat-api';
|
|
4
|
+
import { isForbiddenPropertyName } from 'botframework-webchat-core';
|
|
4
5
|
import PropTypes from 'prop-types';
|
|
5
6
|
import React, { useCallback, useMemo } from 'react';
|
|
6
7
|
import updateIn from 'simple-update-in';
|
|
@@ -89,7 +90,13 @@ const InlineMarkdown = ({ children, onReference, references }) => {
|
|
|
89
90
|
|
|
90
91
|
const html = useMemo(() => {
|
|
91
92
|
const tree = markdownIt.parseInline(children, {
|
|
92
|
-
references: references.reduce(
|
|
93
|
+
references: references.reduce(
|
|
94
|
+
(references, key) =>
|
|
95
|
+
// Mitigated through denylisting.
|
|
96
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
97
|
+
isForbiddenPropertyName(key) ? references : { ...references, [key]: { href: `#${refToHref[key]}` } },
|
|
98
|
+
{}
|
|
99
|
+
)
|
|
93
100
|
});
|
|
94
101
|
|
|
95
102
|
// Turn "<a href="#retry">Retry</a>" into "<button data-ref="retry" type="button">Retry</button>"
|
|
@@ -104,7 +111,16 @@ const InlineMarkdown = ({ children, onReference, references }) => {
|
|
|
104
111
|
|
|
105
112
|
const href = event.target.getAttribute('data-markdown-href');
|
|
106
113
|
|
|
107
|
-
href &&
|
|
114
|
+
href &&
|
|
115
|
+
onReference &&
|
|
116
|
+
onReference(
|
|
117
|
+
createCustomEvent(
|
|
118
|
+
'reference',
|
|
119
|
+
// Mitigated through denylisting.
|
|
120
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
121
|
+
isForbiddenPropertyName(href) ? {} : { data: hrefToRef[href] }
|
|
122
|
+
)
|
|
123
|
+
);
|
|
108
124
|
},
|
|
109
125
|
[hrefToRef, onReference]
|
|
110
126
|
);
|
|
@@ -10,6 +10,10 @@ const INPUTTABLE_KEY = {
|
|
|
10
10
|
Subtract: '-' // Numpad subtract key
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
const INPUTTABLE_KEY_KEYS = Object.keys(INPUTTABLE_KEY);
|
|
14
|
+
|
|
13
15
|
export default function inputtableKey(key) {
|
|
14
|
-
|
|
16
|
+
// Mitigated through allowlisting.
|
|
17
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
18
|
+
return key.length === 1 ? key : INPUTTABLE_KEY_KEYS.includes(key) ? INPUTTABLE_KEY[key] : undefined;
|
|
15
19
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isForbiddenPropertyName } from 'botframework-webchat-core';
|
|
2
|
+
|
|
1
3
|
export default function createCustomEvent(name, eventInitDict) {
|
|
2
4
|
let event;
|
|
3
5
|
|
|
@@ -10,7 +12,11 @@ export default function createCustomEvent(name, eventInitDict) {
|
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
Object.entries(eventInitDict).forEach(([key, value]) => {
|
|
13
|
-
|
|
15
|
+
if (!isForbiddenPropertyName(key)) {
|
|
16
|
+
// Mitigated through denylisting.
|
|
17
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
18
|
+
event[key] = value;
|
|
19
|
+
}
|
|
14
20
|
});
|
|
15
21
|
|
|
16
22
|
return event;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { userAgent } = navigator;
|
|
2
2
|
|
|
3
3
|
// not DRY so that list can be alphabetical
|
|
4
|
+
const android = /Linux; Android/u.test(userAgent);
|
|
4
5
|
const chromium = !/Edge\//u.test(userAgent) && /Chrome\//u.test(userAgent);
|
|
5
6
|
const edgeAnaheim = /Edg\//u.test(userAgent);
|
|
6
7
|
const edgeUWP = /Edge\//u.test(userAgent);
|
|
@@ -10,4 +11,4 @@ const ie11 = /Trident\/7.0/u.test(userAgent);
|
|
|
10
11
|
const chrome = chromium && !edgeAnaheim;
|
|
11
12
|
const safari = !(chrome || edgeUWP || ie11 || firefox);
|
|
12
13
|
|
|
13
|
-
export { chrome, chromium, edgeAnaheim, edgeUWP, firefox, ie11, safari };
|
|
14
|
+
export { android, chrome, chromium, edgeAnaheim, edgeUWP, firefox, ie11, safari };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default function findAncestor(
|
|
2
|
+
node: HTMLElement,
|
|
3
|
+
predicate: (ancestor: HTMLElement) => boolean
|
|
4
|
+
): HTMLElement | undefined {
|
|
5
|
+
let ancestor = node;
|
|
6
|
+
|
|
7
|
+
while ((ancestor = ancestor.parentElement)) {
|
|
8
|
+
if (predicate(ancestor)) {
|
|
9
|
+
return ancestor;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intersects 2 or more arrays and return an array of values which are common to all of them.
|
|
3
|
+
*/
|
|
4
|
+
export default function intersectionOf<T>(arg0: readonly T[], ...args: readonly (readonly T[])[]): T[] {
|
|
5
|
+
return args.reduce<T[]>(
|
|
6
|
+
(interim: T[], arg: readonly T[]) =>
|
|
7
|
+
interim.reduce((intersection: T[], item: T) => {
|
|
8
|
+
arg.includes(item) && intersection.push(item);
|
|
9
|
+
|
|
10
|
+
return intersection;
|
|
11
|
+
}, []),
|
|
12
|
+
[...arg0]
|
|
13
|
+
);
|
|
14
|
+
}
|
package/src/Utils/mapMap.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import { isForbiddenPropertyName } from 'botframework-webchat-core';
|
|
2
|
+
|
|
1
3
|
export default function mapMap(map, mapper) {
|
|
2
4
|
return Object.entries(map).reduce((result, [key, value]) => {
|
|
3
|
-
|
|
5
|
+
if (!isForbiddenPropertyName(key)) {
|
|
6
|
+
// Mitigated through denylisting.
|
|
7
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
8
|
+
result[key] = mapper(value, key);
|
|
9
|
+
}
|
|
4
10
|
|
|
5
11
|
return result;
|
|
6
12
|
}, {});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import computeScrollIntoView from 'compute-scroll-into-view';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Calls `targetElement.scrollIntoView({ block: 'nearest' })`.
|
|
5
|
+
*
|
|
6
|
+
* If browser do not support options for `scrollIntoView`, fallback to polyfill.
|
|
7
|
+
*/
|
|
8
|
+
export default function scrollIntoViewWithBlockNearest(targetElement: HTMLElement): void {
|
|
9
|
+
// Checks if `scrollIntoView` support options or not.
|
|
10
|
+
// - https://github.com/Modernizr/Modernizr/issues/1568#issuecomment-419457972
|
|
11
|
+
// - https://stackoverflow.com/questions/46919627/is-it-possible-to-test-for-scrollintoview-browser-compatibility
|
|
12
|
+
if ('scrollBehavior' in document.documentElement.style) {
|
|
13
|
+
return targetElement.scrollIntoView({ block: 'nearest' });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// We should only move transcript scrollable, and not other scrollable, such as document.body which is from the hosting page.
|
|
17
|
+
const [action] = computeScrollIntoView(targetElement, { block: 'nearest' });
|
|
18
|
+
|
|
19
|
+
action.el.scrollTop = action.top;
|
|
20
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isForbiddenPropertyName } from 'botframework-webchat-core';
|
|
2
|
+
|
|
1
3
|
export default function shallowEquals(x, y) {
|
|
2
4
|
if (x === y) {
|
|
3
5
|
return true;
|
|
@@ -6,5 +8,10 @@ export default function shallowEquals(x, y) {
|
|
|
6
8
|
const xKeys = Object.keys(x);
|
|
7
9
|
const yKeys = Object.keys(y);
|
|
8
10
|
|
|
9
|
-
return
|
|
11
|
+
return (
|
|
12
|
+
xKeys.length === yKeys.length &&
|
|
13
|
+
// Mitigated through denylisting.
|
|
14
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
15
|
+
xKeys.every(key => !isForbiddenPropertyName(key) && yKeys.includes(key) && x[key] === y[key])
|
|
16
|
+
);
|
|
10
17
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// This function is adopted from https://stackoverflow.com/questions/8531940/how-to-detect-if-browser-support-specified-css-pseudo-class
|
|
2
|
+
export default function supportPseudoClass(pseudoClass: string, nonce?: string): boolean {
|
|
3
|
+
const styleElement = document.createElement('style');
|
|
4
|
+
|
|
5
|
+
nonce && styleElement.setAttribute('nonce', nonce);
|
|
6
|
+
document.head.appendChild(styleElement);
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
styleElement.sheet.insertRule('html' + pseudoClass + '{}', 0);
|
|
10
|
+
|
|
11
|
+
return true;
|
|
12
|
+
} catch (error) {
|
|
13
|
+
return false;
|
|
14
|
+
} finally {
|
|
15
|
+
document.head.removeChild(styleElement);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
export default function tabbableElements(element) {
|
|
1
|
+
export default function tabbableElements(element?: HTMLElement): HTMLElement[] {
|
|
2
2
|
// This is an incomplete list, due to the complexity of testing for every scenario.
|
|
3
3
|
// For full-list, please refer to https://allyjs.io/data-tables/focusable.html
|
|
4
|
-
const candidates =
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const candidates: ArrayLike<HTMLElement> =
|
|
5
|
+
element?.querySelectorAll(
|
|
6
|
+
'a[href], area[href], audio[controls], button:not(:disabled), iframe, input:not(:disabled), keygen, select:not(:disabled), summary, textarea:not(:disabled), video[controls], [contenteditable], [tabindex]'
|
|
7
|
+
) || [];
|
|
7
8
|
|
|
8
|
-
return [].filter.call(candidates, element => {
|
|
9
|
+
return ([] as HTMLElement[]).filter.call(candidates, (element: HTMLElement) => {
|
|
9
10
|
const tabIndexAttribute = element.attributes.getNamedItem('tabindex');
|
|
10
11
|
|
|
11
12
|
if (tabIndexAttribute && tabIndexAttribute.specified) {
|