botframework-webchat-fluent-theme 4.18.1-main.20250114.cf2b542 → 4.18.1-main.20250129.b40802d

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botframework-webchat-fluent-theme",
3
- "version": "4.18.1-main.20250114.cf2b542",
3
+ "version": "4.18.1-main.20250129.b40802d",
4
4
  "description": "Fluent theme for Bot Framework Web Chat",
5
5
  "main": "./dist/botframework-webchat-fluent-theme.js",
6
6
  "types": "./dist/botframework-webchat-fluent-theme.d.ts",
@@ -68,15 +68,15 @@
68
68
  "@types/math-random": "^1.0.2",
69
69
  "@types/node": "^20.12.11",
70
70
  "@types/react": "^16.14.60",
71
- "botframework-webchat-base": "4.18.1-main.20250114.cf2b542",
72
- "botframework-webchat-styles": "4.18.1-main.20250114.cf2b542",
71
+ "botframework-webchat-base": "4.18.1-main.20250129.b40802d",
72
+ "botframework-webchat-styles": "4.18.1-main.20250129.b40802d",
73
73
  "tsup": "^8.0.2",
74
74
  "typescript": "^5.4.5"
75
75
  },
76
76
  "dependencies": {
77
- "botframework-webchat-api": "4.18.1-main.20250114.cf2b542",
78
- "botframework-webchat-component": "4.18.1-main.20250114.cf2b542",
79
- "botframework-webchat-core": "4.18.1-main.20250114.cf2b542",
77
+ "botframework-webchat-api": "4.18.1-main.20250129.b40802d",
78
+ "botframework-webchat-component": "4.18.1-main.20250129.b40802d",
79
+ "botframework-webchat-core": "4.18.1-main.20250129.b40802d",
80
80
  "classnames": "2.5.1",
81
81
  "inject-meta-tag": "0.0.1",
82
82
  "math-random": "2.0.1",
@@ -16,8 +16,7 @@
16
16
  }
17
17
 
18
18
  :global(.webchat-fluent) .sendbox__attachment-drop-zone-icon {
19
- height: 36px;
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, { memo, useCallback, useEffect, useRef, useState, type DragEventHandler } from '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,8 +18,14 @@ import { useStyles } from '../../styles';
10
18
 
11
19
  const { useLocalizer } = hooks;
12
20
 
13
- const handleDragOver: DragEventHandler<HTMLDivElement> = event => {
14
- // This is for preventing the browser from opening the dropped file in a new tab.
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
 
@@ -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)) {
@@ -63,18 +79,30 @@ const DropZone = (props: { readonly onFilesAdded: (files: File[]) => void }) =>
63
79
  const handleDragLeave = () => --entranceCounter <= 0 && setDropZoneState(false);
64
80
 
65
81
  const handleDragEnd = () => {
82
+ document.removeEventListener('dragover', handleDragOver);
83
+
66
84
  entranceCounter = 0;
85
+
67
86
  setDropZoneState(false);
68
87
  };
69
88
 
70
- document.addEventListener('dragenter', handleDragEnter, false);
71
- document.addEventListener('dragleave', handleDragLeave, false);
72
- document.addEventListener('dragend', handleDragEnd, false);
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);
73
99
 
74
100
  return () => {
101
+ document.removeEventListener('dragend', handleDragEnd);
75
102
  document.removeEventListener('dragenter', handleDragEnter);
76
103
  document.removeEventListener('dragleave', handleDragLeave);
77
- document.removeEventListener('dragend', handleDragEnd);
104
+ document.removeEventListener('dragover', handleDragOver);
105
+ document.removeEventListener('drop', handleDocumentDrop);
78
106
  };
79
107
  }, [setDropZoneState]);
80
108
 
@@ -103,9 +103,9 @@
103
103
  }
104
104
 
105
105
  :global(.webchat-fluent) .pre-chat-message-activity__card-action-image {
106
+ color: var(--webchat-colorNeutralForeground4);
107
+ font-size: 20px;
106
108
  grid-area: image;
107
- height: 20px;
108
- width: 20px;
109
109
  }
110
110
 
111
111
  :global(.webchat-fluent) .pre-chat-message-activity__card-action-subtitle {
@@ -21,7 +21,7 @@
21
21
  justify-content: center;
22
22
  padding: 3px;
23
23
 
24
- > svg {
24
+ > :global(.webchat__monochrome-image-masker) {
25
25
  font-size: 20px;
26
26
  pointer-events: none;
27
27
  }
@@ -33,6 +33,9 @@
33
33
  --webchat-colorNeutralStencil1: var(--colorNeutralStencil1, #e6e6e6); /* #575757 for dark mode */
34
34
  --webchat-colorNeutralStencil2: var(--colorNeutralStencil2, #fafafa); /* #333333 for dark mode */
35
35
 
36
+ --webchat-colorNeutralShadowAmbient: var(--colorNeutralShadowAmbient, rgba(0, 0, 0, 0.12));
37
+ --webchat-colorNeutralShadowKey: var(--colorNeutralShadowKey, rgba(0, 0, 0, 0.14));
38
+
36
39
  --webchat-colorTransparentBackground: var(--colorTransparentBackground, rgba(0, 0, 0, 0.4));
37
40
 
38
41
  --webchat-colorNeutralStrokeDisabled: var(--colorNeutralStrokeDisabled, #e0e0e0);
@@ -588,5 +591,35 @@
588
591
 
589
592
  /* Monochrome image masker */
590
593
  :global(.webchat-fluent).theme :global(.webchat__monochrome-image-masker) {
591
- background-color: var(--webchat-colorNeutralForeground4);
594
+ background-color: currentColor;
595
+ height: 1em;
596
+ width: 1em;
597
+ }
598
+
599
+ /* Feedback button */
600
+ :global(.webchat-fluent).theme :global(.webchat__thumb-button) {
601
+ color: var(--webchat-colorNeutralForeground1);
602
+
603
+ &:focus-visible {
604
+ outline: var(--webchat-strokeWidthThick) solid var(--webchat-colorStrokeFocus2);
605
+ }
606
+
607
+ &[aria-disabled='true'] {
608
+ color: var(--webchat-colorNeutralForegroundDisabled);
609
+ }
610
+ }
611
+
612
+ /* Tooltip */
613
+ :global(.webchat-fluent).theme :global(.webchat__tooltip) {
614
+ --webchat__tooltip-background: var(--tooltip-background, var(--webchat-colorNeutralBackground1));
615
+
616
+ color: var(--webchat-colorNeutralForeground1);
617
+ filter: drop-shadow(0 0 2px var(--webchat-colorNeutralShadowAmbient))
618
+ drop-shadow(0 4px 8px var(--webchat-colorNeutralShadowKey));
619
+ font-family: var(--webchat-fontFamilyBase);
620
+ font-size: var(--webchat-fontSizeBase200);
621
+ font-weight: var(--webchat-fontWeightRegular);
622
+ line-height: var(--webchat-lineHeightBase200);
623
+ padding: var(--webchat-spacingVerticalSNudge) var(--webchat-spacingHorizontalM);
624
+ transition: opacity var(--webchat-durationNormal) var(--webchat-curveDecelerateMid);
592
625
  }
@@ -1,21 +1,13 @@
1
+ import { Components } from 'botframework-webchat-component';
2
+ import cx from 'classnames';
1
3
  import React, { memo } from 'react';
2
4
 
3
- function AddDocumentIcon(props: Readonly<{ readonly className?: string }>) {
4
- return (
5
- <svg
6
- aria-hidden="true"
7
- className={props.className}
8
- height="1em"
9
- viewBox="0 0 20 20"
10
- width="1em"
11
- xmlns="http://www.w3.org/2000/svg"
12
- >
13
- <path
14
- d="M6 2a2 2 0 0 0-2 2v5.2c.32-.08.66-.15 1-.18V4a1 1 0 0 1 1-1h4v3.5c0 .83.67 1.5 1.5 1.5H15v8a1 1 0 0 1-1 1h-3.6c-.18.36-.4.7-.66 1H14a2 2 0 0 0 2-2V7.41c0-.4-.16-.78-.44-1.06l-3.91-3.91A1.5 1.5 0 0 0 10.59 2H6Zm8.8 5h-3.3a.5.5 0 0 1-.5-.5V3.2L14.8 7ZM10 14.5a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0Zm-4-2a.5.5 0 0 0-1 0V14H3.5a.5.5 0 0 0 0 1H5v1.5a.5.5 0 0 0 1 0V15h1.5a.5.5 0 0 0 0-1H6v-1.5Z"
15
- fill="currentColor"
16
- />
17
- </svg>
18
- );
5
+ const { MonochromeImageMasker } = Components;
6
+
7
+ const addDocumentIcon = `data:image/svg+xml;utf8,${encodeURIComponent('<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M6 2a2 2 0 0 0-2 2v5.2c.32-.08.66-.15 1-.18V4a1 1 0 0 1 1-1h4v3.5c0 .83.67 1.5 1.5 1.5H15v8a1 1 0 0 1-1 1h-3.6c-.18.36-.4.7-.66 1H14a2 2 0 0 0 2-2V7.41c0-.4-.16-.78-.44-1.06l-3.91-3.91A1.5 1.5 0 0 0 10.59 2H6Zm8.8 5h-3.3a.5.5 0 0 1-.5-.5V3.2L14.8 7ZM10 14.5a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0Zm-4-2a.5.5 0 0 0-1 0V14H3.5a.5.5 0 0 0 0 1H5v1.5a.5.5 0 0 0 1 0V15h1.5a.5.5 0 0 0 0-1H6v-1.5Z"/></svg>')}`;
8
+
9
+ function AddDocumentIcon(props: Readonly<{ className?: string }>) {
10
+ return <MonochromeImageMasker className={cx('icon__add-document', props.className)} src={addDocumentIcon} />;
19
11
  }
20
12
 
21
13
  export default memo(AddDocumentIcon);
@@ -1,21 +1,13 @@
1
+ import { Components } from 'botframework-webchat-component';
2
+ import cx from 'classnames';
1
3
  import React, { memo } from 'react';
2
4
 
3
- function AttachmentIcon(props: Readonly<{ readonly className?: string }>) {
4
- return (
5
- <svg
6
- aria-hidden="true"
7
- className={props.className}
8
- height="1em"
9
- viewBox="0 0 20 20"
10
- width="1em"
11
- xmlns="http://www.w3.org/2000/svg"
12
- >
13
- <path
14
- d="m4.83 10.48 5.65-5.65a3 3 0 0 1 4.25 4.24L8 15.8a1.5 1.5 0 0 1-2.12-2.12l6-6.01a.5.5 0 1 0-.7-.71l-6 6.01a2.5 2.5 0 0 0 3.53 3.54l6.71-6.72a4 4 0 1 0-5.65-5.66L4.12 9.78a.5.5 0 0 0 .7.7Z"
15
- fill="currentColor"
16
- />
17
- </svg>
18
- );
5
+ const { MonochromeImageMasker } = Components;
6
+
7
+ const attachmentIcon = `data:image/svg+xml;utf8,${encodeURIComponent('<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="m4.83 10.48 5.65-5.65a3 3 0 0 1 4.25 4.24L8 15.8a1.5 1.5 0 0 1-2.12-2.12l6-6.01a.5.5 0 1 0-.7-.71l-6 6.01a2.5 2.5 0 0 0 3.53 3.54l6.71-6.72a4 4 0 1 0-5.65-5.66L4.12 9.78a.5.5 0 0 0 .7.7Z"/></svg>')}`;
8
+
9
+ function AttachmentIcon(props: Readonly<{ className?: string }>) {
10
+ return <MonochromeImageMasker className={cx('icon__attachment', props.className)} src={attachmentIcon} />;
19
11
  }
20
12
 
21
13
  export default memo(AttachmentIcon);
@@ -1,21 +1,13 @@
1
+ import { Components } from 'botframework-webchat-component';
2
+ import cx from 'classnames';
1
3
  import React, { memo } from 'react';
2
4
 
5
+ const { MonochromeImageMasker } = Components;
6
+
7
+ const infoSmallIcon = `data:image/svg+xml;utf8,${encodeURIComponent('<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8.5 7.5a.5.5 0 1 0-1 0v3a.5.5 0 0 0 1 0v-3Zm.25-2a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1ZM2 8a6 6 0 1 1 12 0A6 6 0 0 1 2 8Z"/></svg>')}`;
8
+
3
9
  function InfoSmallIcon(props: Readonly<{ readonly className?: string }>) {
4
- return (
5
- <svg
6
- aria-hidden="true"
7
- className={props.className}
8
- height="1em"
9
- viewBox="0 0 16 16"
10
- width="1em"
11
- xmlns="http://www.w3.org/2000/svg"
12
- >
13
- <path
14
- d="M8.5 7.5a.5.5 0 1 0-1 0v3a.5.5 0 0 0 1 0v-3Zm.25-2a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1ZM2 8a6 6 0 1 1 12 0A6 6 0 0 1 2 8Z"
15
- fill="currentColor"
16
- />
17
- </svg>
18
- );
10
+ return <MonochromeImageMasker className={cx('icon__info--small', props.className)} src={infoSmallIcon} />;
19
11
  }
20
12
 
21
13
  export default memo(InfoSmallIcon);
@@ -1,21 +1,13 @@
1
+ import { Components } from 'botframework-webchat-component';
2
+ import cx from 'classnames';
1
3
  import React, { memo } from 'react';
2
4
 
5
+ const { MonochromeImageMasker } = Components;
6
+
7
+ const sendIcon = `data:image/svg+xml;utf8,${encodeURIComponent('<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2.18 2.11a.5.5 0 0 1 .54-.06l15 7.5a.5.5 0 0 1 0 .9l-15 7.5a.5.5 0 0 1-.7-.58L3.98 10 2.02 2.63a.5.5 0 0 1 .16-.52Zm2.7 8.39-1.61 6.06L16.38 10 3.27 3.44 4.88 9.5h6.62a.5.5 0 1 1 0 1H4.88Z"/></svg>')}`;
8
+
3
9
  function SendIcon(props: Readonly<{ readonly className?: string }>) {
4
- return (
5
- <svg
6
- aria-hidden="true"
7
- className={props.className}
8
- height="1em"
9
- viewBox="0 0 20 20"
10
- width="1em"
11
- xmlns="http://www.w3.org/2000/svg"
12
- >
13
- <path
14
- d="M2.18 2.11a.5.5 0 0 1 .54-.06l15 7.5a.5.5 0 0 1 0 .9l-15 7.5a.5.5 0 0 1-.7-.58L3.98 10 2.02 2.63a.5.5 0 0 1 .16-.52Zm2.7 8.39-1.61 6.06L16.38 10 3.27 3.44 4.88 9.5h6.62a.5.5 0 1 1 0 1H4.88Z"
15
- fill="currentColor"
16
- />
17
- </svg>
18
- );
10
+ return <MonochromeImageMasker className={cx('icon__send', props.className)} src={sendIcon} />;
19
11
  }
20
12
 
21
13
  export default memo(SendIcon);
@@ -1,21 +1,13 @@
1
+ import { Components } from 'botframework-webchat-component';
2
+ import cx from 'classnames';
1
3
  import React, { memo } from 'react';
2
4
 
5
+ const { MonochromeImageMasker } = Components;
6
+
7
+ const telephoneKeypadIcon = `data:image/svg+xml;utf8,${encodeURIComponent('<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M6 5.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5Zm0 4a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5ZM7.25 12a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0ZM10 5.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5ZM11.25 8a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0ZM10 13.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5ZM11.25 16a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0ZM14 5.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5ZM15.25 8a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0ZM14 13.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5Z"/></svg>')}`;
8
+
3
9
  function TelephoneKeypadIcon(props: Readonly<{ readonly className?: string }>) {
4
- return (
5
- <svg
6
- aria-hidden="true"
7
- className={props.className}
8
- height="1em"
9
- viewBox="0 0 20 20"
10
- width="1em"
11
- xmlns="http://www.w3.org/2000/svg"
12
- >
13
- <path
14
- d="M6 5.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5Zm0 4a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5ZM7.25 12a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0ZM10 5.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5ZM11.25 8a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0ZM10 13.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5ZM11.25 16a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0ZM14 5.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5ZM15.25 8a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0ZM14 13.25a1.25 1.25 0 1 0 0-2.5 1.25 1.25 0 0 0 0 2.5Z"
15
- fill="currentColor"
16
- />
17
- </svg>
18
- );
10
+ return <MonochromeImageMasker className={cx('icon__telephone-keypad', props.className)} src={telephoneKeypadIcon} />;
19
11
  }
20
12
 
21
13
  export default memo(TelephoneKeypadIcon);
@@ -1,4 +1,4 @@
1
- import type { ActivityMiddleware } from 'botframework-webchat-api';
1
+ import { type ActivityMiddleware, type StyleOptions } from 'botframework-webchat-api';
2
2
  import { Components } from 'botframework-webchat-component';
3
3
  import { WebChatDecorator } from 'botframework-webchat-component/decorator';
4
4
  import React, { memo, type ReactNode } from 'react';
@@ -43,11 +43,20 @@ const sendBoxMiddleware = [() => () => () => PrimarySendBox];
43
43
 
44
44
  const styles = createStyles();
45
45
 
46
+ const fluentStyleOptions: StyleOptions = Object.freeze({
47
+ feedbackActionsPlacement: 'activity-actions'
48
+ });
49
+
46
50
  const FluentThemeProvider = ({ children, variant = 'fluent' }: Props) => (
47
51
  <VariantComposer variant={variant}>
48
52
  <WebChatTheme>
49
53
  <TelephoneKeypadProvider>
50
- <ThemeProvider activityMiddleware={activityMiddleware} sendBoxMiddleware={sendBoxMiddleware} styles={styles}>
54
+ <ThemeProvider
55
+ activityMiddleware={activityMiddleware}
56
+ sendBoxMiddleware={sendBoxMiddleware}
57
+ styleOptions={fluentStyleOptions}
58
+ styles={styles}
59
+ >
51
60
  <WebChatDecorator>{children}</WebChatDecorator>
52
61
  </ThemeProvider>
53
62
  </TelephoneKeypadProvider>