Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/component/src/Composer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import useTheme from './providers/Theme/useTheme';
import createDefaultSendBoxMiddleware from './SendBox/createMiddleware';
import createDefaultSendBoxToolbarMiddleware from './SendBoxToolbar/createMiddleware';
import createStyleSet from './Styles/createStyleSet';
import WebChatTheme from './Styles/WebChatTheme';
import useCustomPropertiesClassName from './Styles/useCustomPropertiesClassName';
import { type ContextOf } from './types/ContextOf';
import { type FocusTranscriptInit } from './types/internal/FocusTranscriptInit';
Expand Down Expand Up @@ -318,7 +319,7 @@ ComposerCore.propTypes = {

type ComposerProps = APIComposerProps & ComposerCoreProps;

const Composer = ({
const InternalComposer = ({
activityMiddleware,
activityStatusMiddleware,
attachmentForScreenReaderMiddleware,
Expand Down Expand Up @@ -486,6 +487,12 @@ const Composer = ({
);
};

const Composer = (props: ComposerProps) => (
<WebChatTheme>
<InternalComposer {...props} />
</WebChatTheme>
);

Composer.defaultProps = {
...APIComposer.defaultProps,
...ComposerCore.defaultProps,
Expand Down
36 changes: 36 additions & 0 deletions packages/component/src/Icon/ComponentIcon.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
:global(.webchat) .component-icon {
min-width: var(--webchat__component-icon--size, 1em);
min-height: var(--webchat__component-icon--size, 1em);
place-self: center;

/* Use the image as texture. */
background-image: var(--webchat__component-icon--image, none);
background-position: center;
background-repeat: no-repeat;
background-size: var(--webchat__component-icon--size, 1em);

/* If image is not set, fallback to solid color. */
background-color: var(--webchat__component-icon--color, transparent);

/* 3. Set the mask if any. */
-webkit-mask-image: var(--webchat__component-icon--mask);
-webkit-mask-position: center;
-webkit-mask-repeat: no-repeat;
-webkit-mask-size: var(--webchat__component-icon--size, 1em);
mask-image: var(--webchat__component-icon--mask);
mask-position: center;
mask-repeat: no-repeat;
mask-size: var(--webchat__component-icon--size, 1em);
}

/* #region: Appearance */
:global(.webchat) .appearance--text {
--webchat__component-icon--color: currentColor;
}
/* #endregion */

/* #region: Icons */
:global(.webchat) .icon--dismiss {
--webchat__component-icon--mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="m4.09 4.22.06-.07a.5.5 0 0 1 .63-.06l.07.06L10 9.29l5.15-5.14a.5.5 0 0 1 .63-.06l.07.06c.18.17.2.44.06.63l-.06.07L10.71 10l5.14 5.15c.18.17.2.44.06.63l-.06.07a.5.5 0 0 1-.63.06l-.07-.06L10 10.71l-5.15 5.14a.5.5 0 0 1-.63.06l-.07-.06a.5.5 0 0 1-.06-.63l.06-.07L9.29 10 4.15 4.85a.5.5 0 0 1-.06-.63l.06-.07-.06.07Z"/></svg>');
}
/* #endregion */
35 changes: 35 additions & 0 deletions packages/component/src/Icon/ComponentIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { validateProps } from 'botframework-webchat-react-valibot';
import { useStyles } from 'botframework-webchat-styles/react';
import cx from 'classnames';
import React, { memo } from 'react';
import { object, optional, pipe, readonly, string, type InferInput } from 'valibot';

import createIconComponent from '../Utils/createIconComponent';
import styles from './ComponentIcon.module.css';

const componentIconPropsSchema = pipe(
object({
appearance: optional(string()),
className: optional(string()),
icon: optional(string()),
size: optional(string())
}),
readonly()
);

type ComponentIconProps = InferInput<typeof componentIconPropsSchema>;

function BaseComponentIcon(props: ComponentIconProps) {
const { className } = validateProps(componentIconPropsSchema, props);

const classNames = useStyles(styles);

return <div className={cx(classNames['component-icon'], className)} />;
}

const ComponentIcon = createIconComponent(styles, BaseComponentIcon);

ComponentIcon.displayName = 'ComponentIcon';

export default memo(ComponentIcon);
export { componentIconPropsSchema, type ComponentIconProps };

Check failure on line 35 in packages/component/src/Icon/ComponentIcon.tsx

View workflow job for this annotation

GitHub Actions / Static code analysis

Delete `··`
1 change: 1 addition & 0 deletions packages/component/src/Icon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as ComponentIcon } from './ComponentIcon';
41 changes: 0 additions & 41 deletions packages/component/src/ModdableIcon/ModdableIcon.tsx

This file was deleted.

25 changes: 0 additions & 25 deletions packages/component/src/ModdableIcon/ModdableIconStyle.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
:global(.webchat) .send-box-attachment-bar {
grid-area: attachment-bar;

&.send-box-attachment-bar--as-list-item {
max-height: var(--webchat__max-height--send-box-attachment-bar);
overflow-y: auto;
scrollbar-gutter: stable;
scrollbar-width: thin;
}

&.send-box-attachment-bar--as-thumbnail {
overflow-x: auto;
}

.send-box-attachment-bar__box {
gap: 4px;
}

&.send-box-attachment-bar--as-list-item .send-box-attachment-bar__box {
display: grid;
grid-template-columns: 1fr 1fr;

&:not(:empty) {
padding: 4px;
}
}

&.send-box-attachment-bar--as-thumbnail .send-box-attachment-bar__box {
display: flex;
scrollbar-width: thin;
}
}
20 changes: 10 additions & 10 deletions packages/component/src/SendBox/AttachmentBar/AttachmentBar.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { hooks } from 'botframework-webchat-api';
import { validateProps } from 'botframework-webchat-react-valibot';
import classNames from 'classnames';
import cx from 'classnames';
import React, { memo, useCallback, useMemo } from 'react';
import { useRefFrom } from 'use-ref-from';
import { type InferInput, object, optional, pipe, readonly, string } from 'valibot';
import { useStyles } from 'botframework-webchat-styles/react';
import { object, optional, pipe, readonly, string, type InferInput } from 'valibot';

import { useStyleSet } from '../../hooks';
import styles from './AttachmentBar.module.css';
import testIds from '../../testIds';
import AttachmentBarItem from './AttachmentBarItem';

Expand All @@ -24,7 +25,7 @@ function SendBoxAttachmentBar(props: SendBoxAttachmentBarProps) {
const { className } = validateProps(sendBoxAttachmentBarPropsSchema, props);

const [sendBoxAttachments, setSendBoxAttachments] = useSendBoxAttachments();
const [{ sendBoxAttachmentBar: sendBoxAttachmentBarClassName }] = useStyleSet();
const classNames = useStyles(styles);
const [{ sendBoxAttachmentBarMaxThumbnail }] = useStyleOptions();

const mode = useMemo(
Expand All @@ -45,18 +46,17 @@ function SendBoxAttachmentBar(props: SendBoxAttachmentBarProps) {
return (
sendBoxAttachments.length > 0 && (
<div
className={classNames(
sendBoxAttachmentBarClassName,
'webchat__send-box-attachment-bar',
className={cx(
classNames['send-box-attachment-bar'],
{
'webchat__send-box-attachment-bar--as-list-item': mode === 'list item',
'webchat__send-box-attachment-bar--as-thumbnail': mode === 'thumbnail'
[classNames['send-box-attachment-bar--as-list-item']]: mode === 'list item',
[classNames['send-box-attachment-bar--as-thumbnail']]: mode === 'thumbnail'
},
className
)}
data-testid={testIds.sendBoxAttachmentBar}
>
<div className="webchat__send-box-attachment-bar__box">
<div className={classNames['send-box-attachment-bar__box']}>
{sendBoxAttachments.map((attachment, index) => (
// eslint-disable-next-line react/no-array-index-key
<AttachmentBarItem attachment={attachment} key={index} mode={mode} onDelete={handleAttachmentDelete} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/* #region List item */
:global(.webchat) .send-box-attachment-bar-item {
display: grid;
flex-shrink: 0;
grid-template-rows: auto;
font-family: var(--webchat__font--primary);

&.send-box-attachment-bar-item--as-list-item {
border-radius: 4px;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.14), 0px 0px 2px rgba(0, 0, 0, 0.12);
grid-template-areas: 'body auxiliary';
grid-template-columns: 1fr auto;
padding: 2px;
}

&.send-box-attachment-bar-item--as-thumbnail {
aspect-ratio: 1 / 1;
border: solid 1px rgba(0, 0, 0, 0.25); /* Figma has border-width of 0.96px. */
border-radius: 8px; /* Figma is 7.68px. */
grid-template-areas: 'body';
grid-template-columns: auto;
height: 80px; /* <= 87px would fit white-label design with 3 thumbnails. */
overflow: hidden;
}
}
/* #endregion */

/* #region Delete button */
:global(.webchat) .send-box-attachment-bar-item__delete-button {
/* https://react.fluentui.dev/?path=/docs/theme-colors--docs */
appearance: none;
align-items: center;
background-color: White; /* Background/colorNeutralBackground1 */
border-color: #d1d1d1; /* Stroke/colorNeutralStroke1 */
border-radius: 4px; /* BorderRadiusXS is not defined in Fluent UI, guessing it is 4px. */
cursor: pointer;
grid-area: body;
justify-self: end;
opacity: 1;
padding: 0;
transition: opacity 50ms; /* Assume ultra-fast. */
color: #242424; /* Background/colorNeutralForeground1 */
--webchat__component-icon--size: 19px;

&:hover {
background-color: #f5f5f5; /* Background/colorNeutralBackground1Hover */
border-color: #c7c7c7; /* Stroke/colorNeutralStroke1Hover */
}

&:active {
background-color: #e0e0e0; /* Background/colorNeutralBackground1Pressed */
border-color: #c7c7c7; /* Stroke/colorNeutralStroke1Pressed */
}

&:disabled,
&[aria-disabled='true'] {
background-color: #f0f0f0; /* Background/colorNeutralBackgroundDisabled */
border-color: #e0e0e0; /* Stroke/colorNeutralStrokeDisabled */
color: #bdbdbd; /* Stroke/colorNeutralForegroundDisabled */
}
}

@media (prefers-color-scheme: dark) {
:global(.webchat.theme--prefers-color-scheme) .send-box-attachment-bar-item__delete-button {
background-color: #292929; /* Background/colorNeutralBackground1 */
border-color: #666666; /* Stroke/colorNeutralStroke1 */
color: #ffffff; /* Background/colorNeutralBackground1 */

&:hover {
background-color: #3d3d3d; /* Background/colorNeutralBackground1Hover */
border-color: #757575; /* Stroke/colorNeutralStroke1Hover */
}

&:active {
background-color: #1f1f1f; /* Background/colorNeutralBackground1Pressed */
border-color: #6b6b6b; /* Stroke/colorNeutralStroke1Pressed */
}

&:disabled,
&[aria-disabled='true'] {
background-color: #141414; /* Background/colorNeutralBackgroundDisabled */
border-color: #424242; /* Stroke/colorNeutralStrokeDisabled */
color: #5c5c5c; /* Stroke/colorNeutralForegroundDisabled */
}
}
}

:global(.webchat)
.send-box-attachment-bar-item.send-box-attachment-bar-item--as-list-item
.send-box-attachment-bar-item__delete-button {
border: 0;
grid-area: auxiliary;
height: 24px;
width: 24px;

.send-box-attachment-bar-item__dismiss-icon {
--webchat__component-icon--size: 7px;
--webchat__component-icon--mask: url('data:image/svg+xml;utf8,<svg viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg"><path d="M0.0885911 0.215694L0.146447 0.146447C0.320013 -0.0271197 0.589437 -0.046405 0.784306 0.0885911L0.853553 0.146447L4 3.293L7.14645 0.146447C7.34171 -0.0488154 7.65829 -0.0488154 7.85355 0.146447C8.04882 0.341709 8.04882 0.658291 7.85355 0.853553L4.707 4L7.85355 7.14645C8.02712 7.32001 8.0464 7.58944 7.91141 7.78431L7.85355 7.85355C7.67999 8.02712 7.41056 8.0464 7.21569 7.91141L7.14645 7.85355L4 4.707L0.853553 7.85355C0.658291 8.04882 0.341709 8.04882 0.146447 7.85355C-0.0488154 7.65829 -0.0488154 7.34171 0.146447 7.14645L3.293 4L0.146447 0.853553C-0.0271197 0.679987 -0.046405 0.410563 0.0885911 0.215694L0.146447 0.146447L0.0885911 0.215694Z"/></svg>');
}
}

:global(.webchat)
.send-box-attachment-bar-item.send-box-attachment-bar-item--as-thumbnail
.send-box-attachment-bar-item__delete-button {
border-style: solid; /* Border color will be set elsewhere. */
border-width: 1px; /* Figma has border-width of 0.96px. */
grid-area: body; /* This was already set, but keeping for explicitness from original */
height: 23px; /* Figma is 23.04px. */
margin: 8px; /* Figma is 7.68px. */
width: 23px; /* Figma is 23.04px. */
}

@media not (prefers-reduced-motion: reduce) {
:global(.webchat)
.send-box-attachment-bar-item.send-box-attachment-bar-item--as-thumbnail:not(:hover):not(:focus-within)
.send-box-attachment-bar-item__delete-button {
opacity: 0;
}
}
/* #endregion */

/* #region Preview */
:global(.webchat) .send-box-attachment-bar-item__preview {
align-items: center;
display: grid;
grid-area: body;
overflow: hidden;
}

:global(.webchat)
.send-box-attachment-bar-item.send-box-attachment-bar-item--as-list-item
.send-box-attachment-bar-item__preview {
padding-inline: 8px;
}
/* #endregion */
Loading
Loading