Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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())
Comment thread
OEvgeny marked this conversation as resolved.
}),
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 };
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 {
Comment thread
OEvgeny marked this conversation as resolved.
Outdated
display: grid;
grid-template-columns: 1fr 1fr;

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

&.send-box-attachment-bar--as-thumbnail .send-box-attachment-bar--box {
Comment thread
OEvgeny marked this conversation as resolved.
Outdated
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