botframework-webchat-fluent-theme 4.17.1 → 4.18.1-hotfix.20260127.b53acdf
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/dist/botframework-webchat-fluent-theme.css.map +1 -1
- package/dist/botframework-webchat-fluent-theme.d.mts +23 -5
- package/dist/botframework-webchat-fluent-theme.d.ts +23 -5
- package/dist/botframework-webchat-fluent-theme.development.css.map +1 -1
- package/dist/botframework-webchat-fluent-theme.development.js +11 -1
- package/dist/botframework-webchat-fluent-theme.development.js.map +1 -1
- package/dist/botframework-webchat-fluent-theme.js +1 -1
- package/dist/botframework-webchat-fluent-theme.js.map +1 -1
- package/dist/botframework-webchat-fluent-theme.mjs +1 -1
- package/dist/botframework-webchat-fluent-theme.mjs.map +1 -1
- package/dist/botframework-webchat-fluent-theme.production.min.css.map +1 -1
- package/dist/botframework-webchat-fluent-theme.production.min.js +11 -1
- package/dist/botframework-webchat-fluent-theme.production.min.js.map +1 -1
- package/package.json +27 -26
- package/src/bundle.ts +2 -0
- package/src/components/activity/ActivityDecorator.module.css +432 -0
- package/src/components/activity/ActivityDecorator.tsx +26 -0
- package/src/components/activity/ActivityLoader.module.css +10 -0
- package/src/components/activity/ActivityLoader.tsx +21 -0
- package/src/components/activity/CopilotMessageHeader.module.css +38 -0
- package/src/components/activity/CopilotMessageHeader.tsx +49 -0
- package/src/components/activity/index.ts +2 -0
- package/src/components/activity/private/isAIGeneratedActivity.ts +5 -0
- package/src/components/activity/private/useActivityAuthor.ts +16 -0
- package/src/components/activity/private/useActivityStyleOptions.ts +19 -0
- package/src/components/assets/AssetComposer.tsx +37 -0
- package/src/components/assets/AssetName.ts +1 -0
- package/src/components/assets/SlidingDots.tsx +61 -0
- package/src/components/assets/private/Context.ts +24 -0
- package/src/components/assets/private/useAssetURL.ts +12 -0
- package/src/components/assets/private/useContext.ts +8 -0
- package/src/components/dropZone/DropZone.module.css +3 -4
- package/src/components/dropZone/DropZone.tsx +41 -6
- package/src/components/linerActivity/index.ts +2 -0
- package/src/components/linerActivity/private/LinerActivity.tsx +20 -0
- package/src/components/linerActivity/private/LinerMessageActivity.module.css +28 -0
- package/src/components/linerActivity/private/isLinerMessageActivity.ts +7 -0
- package/src/components/preChatActivity/PreChatMessageActivity.module.css +56 -0
- package/src/components/preChatActivity/PreChatMessageActivity.tsx +60 -0
- package/src/components/preChatActivity/StarterPromptsCardAction.module.css +126 -0
- package/src/components/preChatActivity/StarterPromptsCardAction.tsx +75 -0
- package/src/components/preChatActivity/StarterPromptsToolbar.module.css +18 -0
- package/src/components/preChatActivity/StarterPromptsToolbar.tsx +47 -0
- package/src/components/preChatActivity/index.tsx +2 -0
- package/src/components/preChatActivity/isPreChatMessageActivity.ts +7 -0
- package/src/components/sendBox/Attachments.module.css +1 -1
- package/src/components/sendBox/Attachments.tsx +5 -4
- package/src/components/sendBox/ErrorMessage.tsx +15 -4
- package/src/components/sendBox/SendBox.module.css +21 -6
- package/src/components/sendBox/SendBox.tsx +95 -56
- package/src/components/sendBox/TextArea.module.css +27 -8
- package/src/components/sendBox/TextArea.tsx +60 -31
- package/src/components/sendBox/Toolbar.module.css +15 -7
- package/src/components/sendBox/Toolbar.tsx +17 -7
- package/src/components/sendBox/index.tsx +1 -1
- package/src/components/sendBox/private/useSubmitError.ts +17 -4
- package/src/components/sendBox/private/useTranscriptNavigation.ts +53 -0
- package/src/components/suggestedActions/AccessibleButton.tsx +15 -13
- package/src/components/suggestedActions/SuggestedAction.module.css +14 -13
- package/src/components/suggestedActions/SuggestedAction.tsx +7 -4
- package/src/components/suggestedActions/SuggestedActions.module.css +2 -3
- package/src/components/suggestedActions/SuggestedActions.tsx +49 -46
- package/src/components/telephoneKeypad/private/Button.module.css +2 -3
- package/src/components/telephoneKeypad/private/Button.tsx +1 -5
- package/src/components/telephoneKeypad/private/TelephoneKeypad.module.css +0 -1
- package/src/components/telephoneKeypad/private/TelephoneKeypad.tsx +4 -8
- package/src/components/telephoneKeypad/types.ts +1 -1
- package/src/components/theme/Theme.module.css +665 -15
- package/src/components/theme/Theme.tsx +4 -3
- package/src/components/typingIndicator/SlidingDotsTypingIndicator.module.css +12 -0
- package/src/components/typingIndicator/SlidingDotsTypingIndicator.tsx +19 -0
- package/src/env.d.ts +1 -7
- package/src/external.umd/botframework-webchat-api/decorator.ts +1 -0
- package/src/external.umd/botframework-webchat-component/decorator.ts +1 -0
- package/src/external.umd/botframework-webchat-component/index.ts +5 -0
- package/src/external.umd/botframework-webchat-component/internal.ts +1 -0
- package/src/icons/AddDocumentIcon.tsx +8 -16
- package/src/icons/AttachmentIcon.tsx +8 -16
- package/src/icons/InfoSmallIcon.tsx +7 -15
- package/src/icons/SendIcon.tsx +7 -15
- package/src/icons/TelephoneKeypadIcon.tsx +7 -15
- package/src/index.ts +2 -4
- package/src/private/FluentThemeProvider.tsx +91 -10
- package/src/private/VariantComposer.ts +29 -0
- package/src/private/createComposer.tsx +16 -0
- package/src/private/useVariants.ts +7 -0
- package/src/styles/createStyles.ts +5 -0
- package/src/styles/index.ts +3 -2
- package/src/styles/useStyles.ts +2 -19
- package/src/styles/useVariantClassName.ts +16 -0
- package/src/testIds.ts +3 -0
- package/src/tsconfig.json +12 -10
- package/src/types/PropsOf.ts +2 -5
- package/src/external.umd/botframework-webchat-component.ts +0 -4
- package/src/styles/injectStyle.ts +0 -9
- /package/src/external.umd/{botframework-webchat-api.ts → botframework-webchat-api/index.ts} +0 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { getOrgSchemaMessage, type WebChatActivity } from 'botframework-webchat-core';
|
|
2
|
+
|
|
3
|
+
export default function isAIGeneratedActivity(activity: undefined | WebChatActivity) {
|
|
4
|
+
return !!(activity && getOrgSchemaMessage(activity?.entities || [])?.keywords?.includes('AIGeneratedContent'));
|
|
5
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { getOrgSchemaMessage, type WebChatActivity } from 'botframework-webchat-core';
|
|
3
|
+
|
|
4
|
+
export default function useActivityAuthor(activity?: WebChatActivity | undefined) {
|
|
5
|
+
return useMemo(() => {
|
|
6
|
+
const entity = getOrgSchemaMessage(activity?.entities || []);
|
|
7
|
+
return typeof entity?.author === 'string'
|
|
8
|
+
? {
|
|
9
|
+
'@type': 'Person',
|
|
10
|
+
description: undefined,
|
|
11
|
+
image: undefined,
|
|
12
|
+
name: entity?.author
|
|
13
|
+
}
|
|
14
|
+
: entity?.author;
|
|
15
|
+
}, [activity]);
|
|
16
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { hooks, type WebChatActivity } from 'botframework-webchat-component';
|
|
3
|
+
import { type StrictStyleOptions } from 'botframework-webchat-api';
|
|
4
|
+
|
|
5
|
+
const { useStyleOptions } = hooks;
|
|
6
|
+
|
|
7
|
+
export default function useActivityStyleOptions(activity?: WebChatActivity | undefined) {
|
|
8
|
+
const [styleOptions] = useStyleOptions();
|
|
9
|
+
return useMemo<readonly [Readonly<StrictStyleOptions>]>(
|
|
10
|
+
() =>
|
|
11
|
+
Object.freeze([
|
|
12
|
+
{
|
|
13
|
+
...styleOptions,
|
|
14
|
+
...activity?.channelData?.webChat?.styleOptions
|
|
15
|
+
}
|
|
16
|
+
]),
|
|
17
|
+
[activity?.channelData?.webChat?.styleOptions, styleOptions]
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type ContextOf } from 'botframework-webchat-api';
|
|
2
|
+
import React, { memo, useEffect, useMemo, type ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
import { type AssetName } from './AssetName';
|
|
5
|
+
import Context from './private/Context';
|
|
6
|
+
|
|
7
|
+
type ContextType = ContextOf<typeof Context>;
|
|
8
|
+
|
|
9
|
+
type AssetComposerProps = Readonly<{
|
|
10
|
+
children?: ReactNode | undefined;
|
|
11
|
+
}>;
|
|
12
|
+
|
|
13
|
+
const SLIDING_DOTS_SVG_STRING =
|
|
14
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="400" height="20" viewBox="0 0 400 20"><defs><linearGradient id="a" x1="0" x2="100%" y1="0" y2="0" gradientUnits="userSpaceOnUse"><stop offset="0%"><animate attributeName="stop-color" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="#ad5ae1;#ad5ae1;#0E94E1;#0E94E1;#669fc2;#669fc2;#ad5ae1"/></stop><stop offset="50%"><animate attributeName="stop-color" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="#e9618d;#e9618d;#57AB82;#57AB82;#6377e0;#6377e0;#e9618d"/></stop><stop offset="100%"><animate attributeName="stop-color" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="#fd9e5f;#fd9e5f;#C6C225;#C6C225;#9b80ec;#9b80ec;#fd9e5f"/></stop></linearGradient></defs><g fill="url(#a)"><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.5;0.66;1" repeatCount="indefinite" values="26;26;0;0"/><animate attributeName="width" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;1" repeatCount="indefinite" values="20;20;30;30;20;20"/><animate attributeName="opacity" dur="2s" keyTimes="0;0.5;0.66;1" repeatCount="indefinite" values="1;1;0;0"/></rect><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="62;62;72;72;26;26;0"/><animate attributeName="width" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="104;104;20;20;70;70;20"/><animate attributeName="opacity" dur="2s" keyTimes="0;0.8;1" repeatCount="indefinite" values="1;1;0"/></rect><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="182;182;108;108;112;112;26"/><animate attributeName="width" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;1" repeatCount="indefinite" values="20;20;60;60;20;20"/></rect><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="218;218;184;184;148;148;62"/><animate attributeName="width" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="60;60;80;80;40;40;104"/></rect><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="294;294;280;280;204;204;182"/><animate attributeName="width" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="40;40;20;20;80;80;20"/></rect><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="350;350;316;316;300;300;218"/><animate attributeName="width" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="20;20;60;60;20;20;60"/></rect><rect height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="386;386;392;392;336;336;294"/><animate attributeName="width" dur="2s" keyTimes="0;0.5;0.66;1" repeatCount="indefinite" values="20;20;40;40"/><animate attributeName="opacity" dur="2s" keyTimes="0;0.5;0.66;1" repeatCount="indefinite" values="0;0;1;1"/></rect><rect width="20" height="20" rx="10"><animate attributeName="x" dur="2s" keyTimes="0;0.2;0.33;0.5;0.66;0.8;1" repeatCount="indefinite" values="422;422;428;428;392;392;350"/><animate attributeName="opacity" dur="2s" keyTimes="0;0.8;1" repeatCount="indefinite" values="0;0;1"/></rect></g></svg>';
|
|
15
|
+
|
|
16
|
+
const AssetComposer = memo(({ children }: AssetComposerProps) => {
|
|
17
|
+
const slidingDotsURL = useMemo(
|
|
18
|
+
() => URL.createObjectURL(new Blob([SLIDING_DOTS_SVG_STRING], { type: 'image/svg+xml' })),
|
|
19
|
+
[]
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
useEffect(() => () => URL.revokeObjectURL(slidingDotsURL), [slidingDotsURL]);
|
|
23
|
+
|
|
24
|
+
const context = useMemo<ContextType>(
|
|
25
|
+
() =>
|
|
26
|
+
Object.freeze({
|
|
27
|
+
urlStateMap: new Map<AssetName, readonly [URL]>([['sliding dots', Object.freeze([new URL(slidingDotsURL)])]])
|
|
28
|
+
}),
|
|
29
|
+
[slidingDotsURL]
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return <Context.Provider value={context}>{children}</Context.Provider>;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
AssetComposer.displayName = 'AssetComposer';
|
|
36
|
+
|
|
37
|
+
export default AssetComposer;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type AssetName = 'sliding dots';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { hooks } from 'botframework-webchat-component';
|
|
2
|
+
import React, { memo, useCallback, useEffect, useRef } from 'react';
|
|
3
|
+
import { useRefFrom } from 'use-ref-from';
|
|
4
|
+
|
|
5
|
+
import useAssetURL from './private/useAssetURL';
|
|
6
|
+
|
|
7
|
+
const { useLocalizer, useShouldReduceMotion } = hooks;
|
|
8
|
+
|
|
9
|
+
type SlidingDotsProps = Readonly<{ className: string }>;
|
|
10
|
+
|
|
11
|
+
const SlidingDots = ({ className }: SlidingDotsProps) => {
|
|
12
|
+
const [shouldReduceMotion] = useShouldReduceMotion();
|
|
13
|
+
const [url] = useAssetURL('sliding dots');
|
|
14
|
+
const localize = useLocalizer();
|
|
15
|
+
const objectElementRef = useRef<HTMLObjectElement>(null);
|
|
16
|
+
|
|
17
|
+
const altText = localize('TYPING_INDICATOR_ALT');
|
|
18
|
+
const shouldReduceMotionRef = useRefFrom(shouldReduceMotion);
|
|
19
|
+
|
|
20
|
+
const pauseAnimations = useCallback(() => {
|
|
21
|
+
const contentDocument = objectElementRef.current?.contentDocument;
|
|
22
|
+
const svgElement = contentDocument?.documentElement;
|
|
23
|
+
const { SVGSVGElement } = contentDocument?.defaultView || {};
|
|
24
|
+
|
|
25
|
+
SVGSVGElement && svgElement instanceof SVGSVGElement && svgElement.pauseAnimations();
|
|
26
|
+
}, [objectElementRef]);
|
|
27
|
+
|
|
28
|
+
const unpauseAnimations = useCallback(() => {
|
|
29
|
+
const contentDocument = objectElementRef.current?.contentDocument;
|
|
30
|
+
const svgElement = contentDocument?.documentElement;
|
|
31
|
+
const { SVGSVGElement } = contentDocument?.defaultView || {};
|
|
32
|
+
|
|
33
|
+
SVGSVGElement && svgElement instanceof SVGSVGElement && svgElement.unpauseAnimations();
|
|
34
|
+
}, [objectElementRef]);
|
|
35
|
+
|
|
36
|
+
const pauseOrUnpauseAnimations = useCallback(
|
|
37
|
+
() => (shouldReduceMotionRef.current ? pauseAnimations() : unpauseAnimations()),
|
|
38
|
+
[pauseAnimations, shouldReduceMotionRef, unpauseAnimations]
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
useEffect(pauseOrUnpauseAnimations, [
|
|
42
|
+
pauseOrUnpauseAnimations,
|
|
43
|
+
// Call "pauseOrUnpauseAnimations()" when "shouldReduceMotion" change.
|
|
44
|
+
shouldReduceMotion
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<object
|
|
49
|
+
aria-label={altText}
|
|
50
|
+
className={className}
|
|
51
|
+
data={url.href}
|
|
52
|
+
onLoad={pauseOrUnpauseAnimations}
|
|
53
|
+
ref={objectElementRef}
|
|
54
|
+
type="image/svg+xml"
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
SlidingDots.displayName = 'SlidingDots';
|
|
60
|
+
|
|
61
|
+
export default memo(SlidingDots);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { AssetName } from '../AssetName';
|
|
4
|
+
|
|
5
|
+
type ContextType = Readonly<{
|
|
6
|
+
urlStateMap: ReadonlyMap<AssetName, readonly [URL]>;
|
|
7
|
+
}>;
|
|
8
|
+
|
|
9
|
+
type ContextAsGetter<T extends Record<string, unknown>> =
|
|
10
|
+
T extends Record<infer K, infer V> ? Record<K, { get(): V }> : never;
|
|
11
|
+
|
|
12
|
+
const defaultContextValue: ContextAsGetter<ContextType> = {
|
|
13
|
+
urlStateMap: {
|
|
14
|
+
get() {
|
|
15
|
+
throw new Error('urlMap cannot be used outside of <AssetComposerContext>.');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const Context = createContext<ContextType>(Object.create({}, defaultContextValue));
|
|
21
|
+
|
|
22
|
+
Context.displayName = 'AssetComposerContext';
|
|
23
|
+
|
|
24
|
+
export default Context;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type AssetName } from '../AssetName';
|
|
2
|
+
import useContext from './useContext';
|
|
3
|
+
|
|
4
|
+
export default function useAssetURL(assetName: AssetName): readonly [URL] {
|
|
5
|
+
const urlState = useContext().urlStateMap.get(assetName);
|
|
6
|
+
|
|
7
|
+
if (!urlState) {
|
|
8
|
+
throw new Error(`botframework-webchat-fluent-theme internal: Asset "${assetName}" was not found.`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return urlState;
|
|
12
|
+
}
|
|
@@ -8,16 +8,15 @@
|
|
|
8
8
|
place-content: center;
|
|
9
9
|
place-items: center;
|
|
10
10
|
position: absolute;
|
|
11
|
+
transition: all var(--webchat-durationNormal) var(--webchat-curveDecelerateMid);
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
:global(.webchat-fluent) .sendbox__attachment-drop-zone--droppable {
|
|
14
|
-
background-color:
|
|
15
|
-
color: White
|
|
15
|
+
background-color: var(--webchat-colorBrandBackground2Hover);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
:global(.webchat-fluent) .sendbox__attachment-drop-zone-icon {
|
|
19
|
-
|
|
19
|
+
font-size: 36px;
|
|
20
20
|
/* Set "pointer-events: none" to ignore dragging over the icon. Otherwise, when dragging over the icon; it would disable the "--droppable" modifier.*/
|
|
21
21
|
pointer-events: none;
|
|
22
|
-
width: 36px
|
|
23
22
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { hooks } from 'botframework-webchat-component';
|
|
2
2
|
import cx from 'classnames';
|
|
3
|
-
import React, {
|
|
3
|
+
import React, {
|
|
4
|
+
memo,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
type DragEvent as ReactDragEvent,
|
|
10
|
+
type DragEventHandler
|
|
11
|
+
} from 'react';
|
|
4
12
|
import { useRefFrom } from 'use-ref-from';
|
|
5
13
|
|
|
6
14
|
import { AddDocumentIcon } from '../../icons';
|
|
@@ -10,14 +18,20 @@ import { useStyles } from '../../styles';
|
|
|
10
18
|
|
|
11
19
|
const { useLocalizer } = hooks;
|
|
12
20
|
|
|
13
|
-
const handleDragOver:
|
|
14
|
-
//
|
|
21
|
+
const handleDragOver = (event: ReactDragEvent<unknown> | DragEvent) => {
|
|
22
|
+
// Prevent default dragover behavior to enable drop event triggering.
|
|
23
|
+
// Browsers require this to fire subsequent drop events - without it,
|
|
24
|
+
// they would handle the drop directly (e.g., open files in new tabs).
|
|
25
|
+
// This is needed regardless of whether we prevent default drop behavior,
|
|
26
|
+
// as it ensures our dropzone receives the drop event first. If we allow
|
|
27
|
+
// default drop handling (by not calling preventDefault there), the browser
|
|
28
|
+
// will still process the drop after our event handlers complete.
|
|
15
29
|
event.preventDefault();
|
|
16
30
|
};
|
|
17
31
|
|
|
18
32
|
// Notes: For files dragging from outside of browser, it only tell us if it is a "File" instead of "text/plain" or "text/uri-list".
|
|
19
33
|
// For images dragging inside of browser, it only tell us that it is "text/plain", "text/uri-list" and "text/html". But not "image/*".
|
|
20
|
-
// So we cannot
|
|
34
|
+
// So we cannot allowlist what is droppable.
|
|
21
35
|
// We are using case-insensitive of type "files" so we can drag in WebDriver.
|
|
22
36
|
const isFilesTransferEvent = (event: DragEvent) =>
|
|
23
37
|
!!event.dataTransfer?.types?.some(type => type.toLowerCase() === 'files');
|
|
@@ -47,6 +61,8 @@ const DropZone = (props: { readonly onFilesAdded: (files: File[]) => void }) =>
|
|
|
47
61
|
let entranceCounter = 0;
|
|
48
62
|
|
|
49
63
|
const handleDragEnter = (event: DragEvent) => {
|
|
64
|
+
document.addEventListener('dragover', handleDragOver);
|
|
65
|
+
|
|
50
66
|
entranceCounter++;
|
|
51
67
|
|
|
52
68
|
if (isFilesTransferEvent(event)) {
|
|
@@ -62,12 +78,31 @@ const DropZone = (props: { readonly onFilesAdded: (files: File[]) => void }) =>
|
|
|
62
78
|
|
|
63
79
|
const handleDragLeave = () => --entranceCounter <= 0 && setDropZoneState(false);
|
|
64
80
|
|
|
65
|
-
|
|
66
|
-
|
|
81
|
+
const handleDragEnd = () => {
|
|
82
|
+
document.removeEventListener('dragover', handleDragOver);
|
|
83
|
+
|
|
84
|
+
entranceCounter = 0;
|
|
85
|
+
|
|
86
|
+
setDropZoneState(false);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const handleDocumentDrop = (event: DragEvent) => {
|
|
90
|
+
if (!dropZoneRef.current?.contains(event.target as Node)) {
|
|
91
|
+
handleDragEnd();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
document.addEventListener('dragend', handleDragEnd);
|
|
96
|
+
document.addEventListener('dragenter', handleDragEnter);
|
|
97
|
+
document.addEventListener('dragleave', handleDragLeave);
|
|
98
|
+
document.addEventListener('drop', handleDocumentDrop);
|
|
67
99
|
|
|
68
100
|
return () => {
|
|
101
|
+
document.removeEventListener('dragend', handleDragEnd);
|
|
69
102
|
document.removeEventListener('dragenter', handleDragEnter);
|
|
70
103
|
document.removeEventListener('dragleave', handleDragLeave);
|
|
104
|
+
document.removeEventListener('dragover', handleDragOver);
|
|
105
|
+
document.removeEventListener('drop', handleDocumentDrop);
|
|
71
106
|
};
|
|
72
107
|
}, [setDropZoneState]);
|
|
73
108
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type WebChatActivity } from 'botframework-webchat-core';
|
|
2
|
+
import React, { memo } from 'react';
|
|
3
|
+
import { useStyles } from '../../../styles/index.js';
|
|
4
|
+
import styles from './LinerMessageActivity.module.css';
|
|
5
|
+
|
|
6
|
+
type Props = Readonly<{ activity: WebChatActivity & { type: 'message' } }>;
|
|
7
|
+
|
|
8
|
+
const LinerMessageActivity = ({ activity }: Props) => {
|
|
9
|
+
const classNames = useStyles(styles);
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className={classNames['liner-message-activity']} role="separator">
|
|
13
|
+
<span className={classNames['liner-message-activity__text']}>{activity.text}</span>
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
LinerMessageActivity.displayName = 'LinerMessageActivity';
|
|
19
|
+
|
|
20
|
+
export default memo(LinerMessageActivity);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
:global(.webchat-fluent) .liner-message-activity {
|
|
2
|
+
align-items: center;
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
color: var(--webchat-colorNeutralForeground3);
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: row;
|
|
7
|
+
font-family: var(--webchat__font--primary);
|
|
8
|
+
font-size: var(--webchat-fontSizeBase200);
|
|
9
|
+
font-weight: var(--webchat-fontWeightRegular);
|
|
10
|
+
gap: var(--webchat-spacingHorizontalM);
|
|
11
|
+
line-height: var(--webchat-lineHeightBase200);
|
|
12
|
+
padding: var(--webchat-spacingVerticalSNudge) var(--webchat-spacingHorizontalM);
|
|
13
|
+
text-align: center;
|
|
14
|
+
width: 100%;
|
|
15
|
+
|
|
16
|
+
&::before,
|
|
17
|
+
&::after {
|
|
18
|
+
border-top: var(--webchat-strokeWidthThin) solid var(--webchat-colorNeutralStroke2);
|
|
19
|
+
content: '';
|
|
20
|
+
display: flex;
|
|
21
|
+
flex: auto;
|
|
22
|
+
min-width: 8px;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
:global(.webchat-fluent) .liner-message-activity__text {
|
|
27
|
+
text-align: center;
|
|
28
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type WebChatActivity } from 'botframework-webchat-core';
|
|
2
|
+
|
|
3
|
+
export default function isLinerMessageActivity(
|
|
4
|
+
activity: undefined | WebChatActivity
|
|
5
|
+
): activity is WebChatActivity & { type: 'message'; from: { role: 'channel' } } {
|
|
6
|
+
return !!(activity && activity.from.role === 'channel' && activity.type === 'message' && 'text' in activity);
|
|
7
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
:global(.webchat-fluent) .pre-chat-message-activity {
|
|
2
|
+
box-sizing: border-box;
|
|
3
|
+
display: grid;
|
|
4
|
+
gap: var(--webchat-spacingHorizontalXXXL);
|
|
5
|
+
grid-template-areas: 'body' 'toolbar';
|
|
6
|
+
grid-template-rows: auto auto;
|
|
7
|
+
padding: var(--webchat-spacingHorizontalXXXL) var(--webchat-spacingHorizontalM);
|
|
8
|
+
position: relative;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
:global(.webchat-fluent) .pre-chat-message-activity__body {
|
|
12
|
+
align-items: center;
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-flow: column nowrap;
|
|
15
|
+
font-family: var(--webchat-fontFamilyBase);
|
|
16
|
+
font-size: var(--webchat-fontSizeBase300);
|
|
17
|
+
font-weight: var(--webchat-fontWeightRegular);
|
|
18
|
+
gap: var(--webchat-spacingVerticalXS);
|
|
19
|
+
grid-area: body;
|
|
20
|
+
line-height: var(--webchat-lineHeightBase300);
|
|
21
|
+
text-align: center;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
:global(.webchat-fluent) .pre-chat-message-activity__body--blueprint {
|
|
25
|
+
opacity: 60%;
|
|
26
|
+
|
|
27
|
+
.pre-chat-message-activity__body-avatar {
|
|
28
|
+
filter: grayscale(1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
:global(.webchat-fluent) .pre-chat-message-activity__body-avatar {
|
|
33
|
+
border-radius: var(--webchat-borderRadiusMedium);
|
|
34
|
+
height: 64px;
|
|
35
|
+
margin-block-end: var(--webchat-spacingVerticalM);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
:global(.webchat-fluent) .pre-chat-message-activity__body-title {
|
|
39
|
+
color: var(--webchat-colorNeutralForeground1);
|
|
40
|
+
font-family: inherit;
|
|
41
|
+
font-size: var(--webchat-fontSizeHero700);
|
|
42
|
+
font-weight: var(--webchat-fontWeightSemibold);
|
|
43
|
+
line-height: var(--webchat-lineHeightHero700);
|
|
44
|
+
margin: 0;
|
|
45
|
+
overflow-wrap: anywhere;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
:global(.webchat-fluent) .pre-chat-message-activity__body-subtitle {
|
|
49
|
+
font-size: var(--webchat-fontSizeBase300);
|
|
50
|
+
line-height: var(--webchat-lineHeightBase300);
|
|
51
|
+
overflow-wrap: anywhere;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
:global(.webchat-fluent) .pre-chat-message-activity__toolbar {
|
|
55
|
+
grid-area: toolbar;
|
|
56
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { hooks } from 'botframework-webchat-component';
|
|
2
|
+
import { type WebChatActivity } from 'botframework-webchat-core';
|
|
3
|
+
import cx from 'classnames';
|
|
4
|
+
import React, { memo, useMemo } from 'react';
|
|
5
|
+
import { useStyles } from '../../styles/index.js';
|
|
6
|
+
import styles from './PreChatMessageActivity.module.css';
|
|
7
|
+
import StarterPromptsToolbar from './StarterPromptsToolbar.js';
|
|
8
|
+
import { useActivityAuthor } from '../activity/index.js';
|
|
9
|
+
|
|
10
|
+
type Props = Readonly<{ activity: WebChatActivity & { type: 'message' } }>;
|
|
11
|
+
|
|
12
|
+
const { useLocalizer, useRenderMarkdownAsHTML, useUIState } = hooks;
|
|
13
|
+
|
|
14
|
+
const PreChatMessageActivity = ({ activity }: Props) => {
|
|
15
|
+
const [uiState] = useUIState();
|
|
16
|
+
const classNames = useStyles(styles);
|
|
17
|
+
const renderMarkdownAsHTML = useRenderMarkdownAsHTML();
|
|
18
|
+
const localize = useLocalizer();
|
|
19
|
+
|
|
20
|
+
const author = useActivityAuthor(activity);
|
|
21
|
+
|
|
22
|
+
const html = useMemo(
|
|
23
|
+
() => (renderMarkdownAsHTML ? { __html: renderMarkdownAsHTML(author?.description || '') } : { __html: '' }),
|
|
24
|
+
[author?.description, renderMarkdownAsHTML]
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className={classNames['pre-chat-message-activity']}>
|
|
29
|
+
{author && (
|
|
30
|
+
<div
|
|
31
|
+
className={cx(
|
|
32
|
+
classNames['pre-chat-message-activity__body'],
|
|
33
|
+
uiState === 'blueprint' && classNames['pre-chat-message-activity__body--blueprint']
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
{author.image && (
|
|
37
|
+
<img
|
|
38
|
+
alt={localize('AVATAR_ALT', author.name)}
|
|
39
|
+
className={classNames['pre-chat-message-activity__body-avatar']}
|
|
40
|
+
src={author.image}
|
|
41
|
+
/>
|
|
42
|
+
)}
|
|
43
|
+
{author.name && <h2 className={classNames['pre-chat-message-activity__body-title']}>{author.name}</h2>}
|
|
44
|
+
{author.description && (
|
|
45
|
+
// eslint-disable-next-line react/no-danger
|
|
46
|
+
<div className={classNames['pre-chat-message-activity__body-subtitle']} dangerouslySetInnerHTML={html} />
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
)}
|
|
50
|
+
<StarterPromptsToolbar
|
|
51
|
+
cardActions={activity.suggestedActions?.actions || []}
|
|
52
|
+
className={classNames['pre-chat-message-activity__toolbar']}
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
PreChatMessageActivity.displayName = 'PreChatMessageActivity';
|
|
59
|
+
|
|
60
|
+
export default memo(PreChatMessageActivity);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
:global(.webchat-fluent) .pre-chat-message-activity__card-action-box {
|
|
2
|
+
appearance: none;
|
|
3
|
+
background-color: var(--webchat-colorNeutralBackground1);
|
|
4
|
+
border: 0;
|
|
5
|
+
border-radius: 16px;
|
|
6
|
+
box-shadow: var(--webchat-shadow2);
|
|
7
|
+
color: var(--webchat-colorNeutralForeground1);
|
|
8
|
+
cursor: pointer;
|
|
9
|
+
display: grid;
|
|
10
|
+
gap: var(--webchat-spacingVerticalS);
|
|
11
|
+
grid-template-areas: 'title' 'subtitle';
|
|
12
|
+
grid-template-columns: 1fr;
|
|
13
|
+
grid-template-rows: auto 1fr;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
padding: 16px 20px;
|
|
16
|
+
text-align: left;
|
|
17
|
+
transition: background-color var(--webchat-durationNormal) var(--webchat-curveDecelerateMid);
|
|
18
|
+
user-select: none;
|
|
19
|
+
|
|
20
|
+
&:has(.pre-chat-message-activity__card-action-image) {
|
|
21
|
+
grid-template-areas: 'image title' 'image subtitle';
|
|
22
|
+
grid-template-columns: 20px 1fr;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
&:empty {
|
|
26
|
+
row-gap: 14px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&[aria-disabled='true'],
|
|
30
|
+
&:empty {
|
|
31
|
+
cursor: default;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&[aria-disabled='true']:not(:empty) {
|
|
35
|
+
background-color: var(--webchat-colorNeutralBackgroundDisabled);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&:hover:not([aria-disabled='true'], :empty) {
|
|
39
|
+
background-color: var(--webchat-colorNeutralBackground1Hover);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
&:active:not([aria-disabled='true'], :empty) {
|
|
43
|
+
background-color: var(--webchat-colorNeutralBackground1Pressed);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&:focus-visible {
|
|
47
|
+
outline: solid 2px var(--webchat-colorStrokeFocus2);
|
|
48
|
+
outline-offset: -2px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&:empty::after,
|
|
52
|
+
&:empty::before {
|
|
53
|
+
animation: blueprintAnimation 3s linear infinite;
|
|
54
|
+
background-attachment: fixed;
|
|
55
|
+
background-image: linear-gradient(
|
|
56
|
+
-90deg,
|
|
57
|
+
var(--webchat-colorNeutralStencil1) 0%,
|
|
58
|
+
var(--webchat-colorNeutralStencil2) 50%,
|
|
59
|
+
var(--webchat-colorNeutralStencil1) 100%
|
|
60
|
+
);
|
|
61
|
+
background-size: 300% 100%;
|
|
62
|
+
content: '';
|
|
63
|
+
display: block;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* animation-* needs to position after animation shorthand. */
|
|
67
|
+
@media (prefers-reduced-motion: reduce) {
|
|
68
|
+
&:empty::after,
|
|
69
|
+
&:empty::before {
|
|
70
|
+
animation-delay: -1s;
|
|
71
|
+
animation-play-state: paused;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
&:dir(rtl) {
|
|
76
|
+
&:empty::after,
|
|
77
|
+
&:empty::before {
|
|
78
|
+
animation-direction: reverse;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
&:empty::after {
|
|
83
|
+
border-radius: 16px;
|
|
84
|
+
height: 16px;
|
|
85
|
+
width: 100%;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
&:empty::before {
|
|
89
|
+
border-radius: 18px;
|
|
90
|
+
height: 18px;
|
|
91
|
+
width: 66%;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@keyframes blueprintAnimation {
|
|
96
|
+
from {
|
|
97
|
+
background-position-x: 0%;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
to {
|
|
101
|
+
background-position-x: -300%;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
:global(.webchat-fluent) .pre-chat-message-activity__card-action-image {
|
|
106
|
+
color: var(--webchat-colorNeutralForeground4);
|
|
107
|
+
font-size: 20px;
|
|
108
|
+
grid-area: image;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
:global(.webchat-fluent) .pre-chat-message-activity__card-action-subtitle {
|
|
112
|
+
font-family: var(--webchat-fontFamilyBase);
|
|
113
|
+
font-size: 14px;
|
|
114
|
+
font-weight: var(--webchat-fontWeightRegular);
|
|
115
|
+
grid-area: subtitle;
|
|
116
|
+
line-height: 20px;
|
|
117
|
+
pointer-events: none; /* Links in subtitle are not clickable. */
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
:global(.webchat-fluent) .pre-chat-message-activity__card-action-title {
|
|
121
|
+
font-family: var(--webchat-fontFamilyBase);
|
|
122
|
+
font-size: 14px;
|
|
123
|
+
font-weight: var(--webchat-fontWeightSemibold);
|
|
124
|
+
grid-area: title;
|
|
125
|
+
line-height: 20px;
|
|
126
|
+
}
|