Skip to content

Commit 16e21c3

Browse files
committed
feat: implement chat-bubble style API
1 parent 2795450 commit 16e21c3

File tree

5 files changed

+204
-12
lines changed

5 files changed

+204
-12
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import Box from "@cloudscape-design/components/box";
4+
5+
import { ChatBubble } from "../../lib/components";
6+
import { Page } from "../app/templates";
7+
import { TestBed } from "../app/test-bed";
8+
import { Actions, ChatBubbleAvatarGenAI, ChatBubbleAvatarUser, ChatContainer } from "./util-components";
9+
10+
export default function ChatBubblePage() {
11+
return (
12+
<Page title="Chat bubble">
13+
<TestBed>
14+
<ChatContainer>
15+
{/* Background Color with Dark Mode */}
16+
<ChatBubble
17+
style={{ bubble: { background: "light-dark(#f0f8ff, #1a1a2e)", color: "light-dark(#333, #eee)" } }}
18+
type="incoming"
19+
avatar={<ChatBubbleAvatarGenAI />}
20+
ariaLabel="Background test"
21+
>
22+
Light blue/dark purple background with adaptive text color
23+
</ChatBubble>
24+
25+
{/* Border Styles with Dark Mode */}
26+
<ChatBubble
27+
style={{
28+
bubble: { borderColor: "light-dark(#e74c3c, #ff6b6b)", borderWidth: "2px", borderRadius: "20px" },
29+
}}
30+
type="outgoing"
31+
avatar={<ChatBubbleAvatarUser />}
32+
ariaLabel="Border test"
33+
>
34+
Adaptive red border with rounded corners
35+
</ChatBubble>
36+
37+
{/* Typography with Dark Mode */}
38+
<ChatBubble
39+
style={{ bubble: { fontSize: "18px", fontWeight: "bold", color: "light-dark(#8e44ad, #bb86fc)" } }}
40+
type="incoming"
41+
avatar={<ChatBubbleAvatarGenAI />}
42+
ariaLabel="Typography test"
43+
>
44+
Large bold adaptive purple text
45+
</ChatBubble>
46+
47+
{/* Shadow Effect with Dark Mode */}
48+
<ChatBubble
49+
style={{
50+
bubble: { boxShadow: "10px 5px 5px red" },
51+
}}
52+
type="outgoing"
53+
avatar={<ChatBubbleAvatarUser />}
54+
ariaLabel="Shadow test"
55+
>
56+
Adaptive shadow for elevation
57+
</ChatBubble>
58+
59+
{/* Spacing */}
60+
<ChatBubble
61+
style={{ root: { columnGap: "32px" }, bubble: { paddingBlock: "20px", paddingInline: "24px" } }}
62+
type="incoming"
63+
avatar={<ChatBubbleAvatarGenAI />}
64+
ariaLabel="Spacing test"
65+
>
66+
Wide avatar spacing with generous padding
67+
</ChatBubble>
68+
69+
{/* Loading State with Dark Mode */}
70+
<ChatBubble
71+
style={{
72+
bubble: {
73+
background: "light-dark(#fff3cd, #3d3d00)",
74+
borderColor: "light-dark(#ffc107, #ffeb3b)",
75+
borderWidth: "1px",
76+
},
77+
}}
78+
avatar={<ChatBubbleAvatarGenAI loading={true} />}
79+
type="incoming"
80+
showLoadingBar={true}
81+
ariaLabel="Loading test"
82+
>
83+
<Box color="text-body-secondary">Generating response...</Box>
84+
</ChatBubble>
85+
86+
{/* With Actions and Dark Mode */}
87+
<ChatBubble
88+
style={{ bubble: { background: "light-dark(#e8f5e8, #1b2e1b)", borderRadius: "18px", rowGap: "25px" } }}
89+
avatar={<ChatBubbleAvatarGenAI />}
90+
type="incoming"
91+
actions={<Actions />}
92+
ariaLabel="Actions test"
93+
>
94+
Message with action buttons
95+
</ChatBubble>
96+
97+
{/* All Properties Combined with Dark Mode */}
98+
<ChatBubble
99+
style={{
100+
root: {
101+
columnGap: "25px",
102+
},
103+
bubble: {
104+
background: "light-dark(#ffffff, #2d2d2d)",
105+
color: "light-dark(#1b5e20, #81c784)",
106+
borderColor: "light-dark(#4caf50, #66bb6a)",
107+
borderWidth: "2px",
108+
borderRadius: "24px",
109+
boxShadow: "0 4px 12px rgb(29, 130, 118)",
110+
fontSize: "16px",
111+
fontWeight: "bold",
112+
paddingBlock: "20px",
113+
paddingInline: "30px",
114+
rowGap: "20px",
115+
},
116+
}}
117+
type="outgoing"
118+
avatar={<ChatBubbleAvatarUser />}
119+
actions={<Actions />}
120+
ariaLabel="All properties test"
121+
showLoadingBar={true}
122+
>
123+
All style properties combined
124+
</ChatBubble>
125+
</ChatContainer>
126+
</TestBed>
127+
</Page>
128+
);
129+
}

src/chat-bubble/interfaces.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,31 @@ export interface ChatBubbleProps {
3131
* Useful for when there are multiple consecutive messages coming from the same author.
3232
*/
3333
hideAvatar?: boolean;
34+
35+
/**
36+
* @awsuiSystem core
37+
*/
38+
style?: ChatBubbleProps.Style;
3439
}
3540

3641
export namespace ChatBubbleProps {
3742
export type Type = "incoming" | "outgoing";
43+
export interface Style {
44+
root?: {
45+
columnGap?: string;
46+
};
47+
bubble?: {
48+
background?: string;
49+
borderColor?: string;
50+
borderRadius?: string;
51+
borderWidth?: string;
52+
boxShadow?: string;
53+
color?: string;
54+
fontSize?: string;
55+
fontWeight?: string;
56+
rowGap?: string;
57+
paddingBlock?: string;
58+
paddingInline?: string;
59+
};
60+
}
3861
}

src/chat-bubble/internal.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { getDataAttributes } from "../internal/base-component/get-data-attribute
77
import { InternalBaseComponentProps } from "../internal/base-component/use-base-component";
88
import { InternalLoadingBar } from "../loading-bar/internal";
99
import { ChatBubbleProps } from "./interfaces.js";
10+
import { getBubbleStyle, getChatBubbleRootStyle } from "./style";
1011

1112
import styles from "./styles.css.js";
1213

@@ -20,6 +21,7 @@ export default function InternalChatBubble({
2021
showLoadingBar,
2122
hideAvatar = false,
2223
ariaLabel,
24+
style,
2325
__internalRootRef = null,
2426
...rest
2527
}: InternalChatBubbleProps) {
@@ -43,23 +45,22 @@ export default function InternalChatBubble({
4345
ref={__internalRootRef}
4446
role="group"
4547
aria-label={ariaLabel}
48+
style={getChatBubbleRootStyle(style)}
4649
>
4750
{avatar && (
4851
<div ref={avatarRef} className={clsx(styles.avatar, hideAvatar && styles.hide)}>
4952
{avatar}
5053
</div>
5154
)}
5255

53-
<div
54-
className={clsx(styles["message-area"], styles[`chat-bubble-type-${type}`], {
55-
[styles["with-loading-bar"]]: showLoadingBar,
56-
})}
57-
>
56+
<div className={clsx(styles["message-area"], styles[`chat-bubble-type-${type}`])} style={getBubbleStyle(style)}>
5857
<div className={styles.content}>{children}</div>
59-
6058
{actions && <div className={styles.actions}>{actions}</div>}
61-
62-
{showLoadingBar && <InternalLoadingBar variant="gen-ai-masked" />}
59+
{showLoadingBar && (
60+
<div className={styles["loading-bar-wrapper"]}>
61+
<InternalLoadingBar variant="gen-ai-masked" />
62+
</div>
63+
)}
6364
</div>
6465
</div>
6566
);

src/chat-bubble/style.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import { SYSTEM } from "../internal/environment";
4+
import { ChatBubbleProps } from "./interfaces";
5+
6+
export function getChatBubbleRootStyle(style: ChatBubbleProps.Style | undefined) {
7+
if (SYSTEM !== "core") {
8+
return {};
9+
}
10+
11+
return {
12+
columnGap: style?.root?.columnGap,
13+
};
14+
}
15+
16+
export function getBubbleStyle(style: ChatBubbleProps.Style | undefined) {
17+
if (SYSTEM !== "core") {
18+
return {};
19+
}
20+
21+
return {
22+
background: style?.bubble?.background,
23+
borderColor: style?.bubble?.borderColor,
24+
borderRadius: style?.bubble?.borderRadius,
25+
borderStyle: style?.bubble?.borderWidth ? "solid" : undefined,
26+
borderWidth: style?.bubble?.borderWidth,
27+
boxShadow: style?.bubble?.boxShadow,
28+
color: style?.bubble?.color,
29+
fontSize: style?.bubble?.fontSize,
30+
fontWeight: style?.bubble?.fontWeight,
31+
paddingBlock: style?.bubble?.paddingBlock,
32+
paddingInline: style?.bubble?.paddingInline,
33+
rowGap: style?.bubble?.rowGap,
34+
};
35+
}

src/chat-bubble/styles.scss

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
}
1919

2020
.message-area {
21+
position: relative;
2122
display: flex;
2223
flex-direction: column;
2324
gap: cs.$space-scaled-s;
@@ -30,10 +31,6 @@
3031
border-end-start-radius: cs.$border-radius-chat-bubble;
3132
border-end-end-radius: cs.$border-radius-chat-bubble;
3233

33-
&.with-loading-bar {
34-
padding-block-end: 0;
35-
}
36-
3734
&.chat-bubble-type-outgoing {
3835
color: cs.$color-text-chat-bubble-outgoing;
3936
background-color: cs.$color-background-chat-bubble-outgoing;
@@ -45,6 +42,13 @@
4542
}
4643
}
4744

45+
.loading-bar-wrapper {
46+
position: absolute;
47+
inset-block-end: 0;
48+
inset-inline-start: 0;
49+
inline-size: 100%;
50+
}
51+
4852
.avatar {
4953
padding-block: cs.$space-scaled-xs;
5054

0 commit comments

Comments
 (0)