Skip to content

Commit 8ea9346

Browse files
committed
add expand/shrink option to chat content
1 parent d9cffd3 commit 8ea9346

5 files changed

Lines changed: 109 additions & 13 deletions

File tree

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,11 @@
9191
"n3": "^1.25.1",
9292
"re-resizable": "^6.10.3",
9393
"react": "^16.13.1",
94-
"react-dom": "^16.13.1",
94+
"react-dom": "^16.14.0",
9595
"react-flow-renderer": "9.7.4",
9696
"react-flow-renderer-lts": "npm:react-flow-renderer@^10.3.17",
9797
"react-inlinesvg": "^3.0.3",
98+
"react-is": "^16.13.1",
9899
"react-markdown": "^10.1.0",
99100
"react-markdown-deprecated": "npm:react-markdown@^8.0.7",
100101
"react-syntax-highlighter": "^15.6.1",
@@ -138,6 +139,7 @@
138139
"@types/jshint": "^2.12.4",
139140
"@types/lodash": "^4.17.16",
140141
"@types/n3": "^1.24.2",
142+
"@types/react-is": "^19.0.0",
141143
"@types/react-syntax-highlighter": "^15.5.13",
142144
"@typescript-eslint/eslint-plugin": "^8.30.1",
143145
"@typescript-eslint/parser": "^8.30.1",
@@ -174,8 +176,7 @@
174176
},
175177
"peerDependencies": {
176178
"@blueprintjs/core": ">=5",
177-
"react": ">=16",
178-
"react-dom": ">=16"
179+
"react": ">=16"
179180
},
180181
"resolutions": {
181182
"**/@types/react": "^17.0.85",

src/components/Chat/ChatContent.tsx

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import React from "react";
2+
import { renderToString } from "react-dom/server";
3+
import * as ReactIs from "react-is";
24

35
import { TestableComponent } from "../../components/interfaces";
46
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
57

68
import { Markdown, MarkdownProps } from "./../../cmem/markdown/Markdown";
79
import { DepictionProps } from "./../Depiction/Depiction";
810
import { FlexibleLayoutContainer, FlexibleLayoutItem } from "./../FlexibleLayout";
11+
import { IconButton } from "./../Icon/IconButton";
912
import { Spacing } from "./../Separation/Spacing";
10-
import { HtmlContentBlock, OverflowTextProps } from "./../Typography";
13+
import { HtmlContentBlock, OverflowText, OverflowTextProps } from "./../Typography";
1114

1215
export interface ChatContentProps extends React.HTMLAttributes<HTMLDivElement>, TestableComponent {
1316
/**
@@ -35,12 +38,30 @@ export interface ChatContentProps extends React.HTMLAttributes<HTMLDivElement>,
3538
* If set then the chat bubble only grows to a height of 50% of the viewport.
3639
* In case you need to set other maximum heights then use the `style` property directly.
3740
*/
38-
limitHeight?: boolean;
41+
limitHeight?: React.ReactChild;
3942
/**
4043
* If given then the content is automatically parsed and displayed by our `<Markdown />` component.
4144
* `children` need to a `string` then, otherwise it cannot be parsed.
4245
*/
4346
markdownProps?: Omit<MarkdownProps, "children">;
47+
/**
48+
* Callback handler if content should be expanded.
49+
* Button to shrink/expand is displayed, depending on `shrinked` value.
50+
* If this handler is given then the component never will change the `shrinked` state automatically.
51+
*/
52+
onToggleSize?: () => void;
53+
/**
54+
* Content should dislayed shrinked.
55+
* Button to expand content is displayed.
56+
* Component can reduce content automatically to one line if `autoShrink` is set to `true`.
57+
* If `onToggleSize` handler is not given then `autoShrink=true` is inferred and size toggling is automatically provided.
58+
*/
59+
shrinked?: boolean;
60+
/**
61+
* Children elements are automatically shrinked to one line.
62+
* If `shrinked` are not given then `shrinked=true` is infered.
63+
*/
64+
autoShrink?: boolean;
4465
}
4566

4667
/**
@@ -56,9 +77,56 @@ export const ChatContent = ({
5677
alignment = "left",
5778
limitHeight,
5879
markdownProps,
80+
shrinked,
81+
autoShrink,
82+
onToggleSize,
5983
...otherDivProps
6084
}: ChatContentProps) => {
61-
const content = (
85+
const [displayShrinked, setDispayShrinked] = React.useState<boolean>(
86+
shrinked === true || (autoShrink === true && typeof shrinked === "undefined")
87+
);
88+
89+
const toggleSize = () => {
90+
if (onToggleSize) {
91+
onToggleSize();
92+
} else {
93+
setDispayShrinked(!displayShrinked);
94+
}
95+
};
96+
97+
const content =
98+
markdownProps && typeof children === "string" ? <Markdown {...markdownProps}>{children}</Markdown> : children;
99+
100+
const onlyText = (children: React.ReactNode | React.ReactNode[]): string => {
101+
if (children instanceof Array) {
102+
return children
103+
.map((child: React.ReactNode) => {
104+
return onlyText(child);
105+
})
106+
.join(" ");
107+
}
108+
109+
return React.Children.toArray(children)
110+
.map((child) => {
111+
if (ReactIs.isFragment(child)) {
112+
return onlyText(child.props?.children);
113+
}
114+
if (typeof child === "string") {
115+
return child;
116+
}
117+
if (typeof child === "number") {
118+
return child.toString();
119+
}
120+
if (ReactIs.isElement(child)) {
121+
// for some reasons `renderToString` returns empty string if not wrappe in a `span`
122+
return renderToString(<span>{child}</span>);
123+
}
124+
return "";
125+
})
126+
.join(" ");
127+
};
128+
129+
const chatitem = (
62130
<div
63131
className={
64132
`${eccgui}-chat__content` +
@@ -75,10 +143,12 @@ export const ChatContent = ({
75143
<Spacing size="tiny" />
76144
</HtmlContentBlock>
77145
)}
78-
{markdownProps && typeof children === "string" ? (
79-
<Markdown {...markdownProps}>{children}</Markdown>
146+
{displayShrinked && autoShrink ? (
147+
<OverflowText passDown>
148+
<Markdown removeMarkup>{onlyText(content)}</Markdown>
149+
</OverflowText>
80150
) : (
81-
children
151+
content
82152
)}
83153
</div>
84154
);
@@ -107,7 +177,20 @@ export const ChatContent = ({
107177
{React.cloneElement(avatar, { size: "small", ratio: "1:1", rounded: true, resizing: "cover" })}
108178
</FlexibleLayoutItem>
109179
)}
110-
<FlexibleLayoutItem className={`${eccgui}-chat__content-wrapper`}>{content}</FlexibleLayoutItem>
180+
<FlexibleLayoutItem className={`${eccgui}-chat__content-wrapper`}>{chatitem}</FlexibleLayoutItem>
181+
{(displayShrinked || onToggleSize || autoShrink) && (
182+
<FlexibleLayoutItem
183+
className={`${eccgui}-chat__content-sizetoggle`}
184+
growFactor={0}
185+
shrinkFactor={0}
186+
style={alignment === "right" ? { order: -1 } : undefined}
187+
>
188+
<IconButton
189+
name={displayShrinked ? "toggler-showmore" : "toggler-showless"}
190+
onClick={() => toggleSize()}
191+
/>
192+
</FlexibleLayoutItem>
193+
)}
111194
</FlexibleLayoutContainer>
112195
</div>
113196
);

src/components/Chat/stories/ChatArea.stories.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ Default.args = {
2626
<ChatContent {...ShortChatBubble.args} alignment="right" indentationSize="medium" />,
2727
<ChatContent {...ShortChatBubble.args} avatar={undefined} displayType="free" />,
2828
<ChatContent {...ShortChatBubble.args} alignment="right" indentationSize="medium" />,
29-
<ChatContent {...LongChatBubble.args} />,
29+
<ChatContent {...LongChatBubble.args} autoShrink />,
3030
<ChatContent {...ShortChatBubble.args} alignment="right" indentationSize="medium" />,
3131
<ChatContent {...ShortChatBubble.args} />,
3232
],
33+
autoSpacingSize: "medium",
34+
autoScrollTo: "last",
35+
useAbsoluteSpace: true,
3336
};

src/components/Chat/stories/ChatContent.stories.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ export default {
2929
},
3030
} as Meta<typeof ChatContent>;
3131

32-
const TemplateFull: StoryFn<typeof ChatContent> = (args) => <ChatContent {...args} />;
32+
let update = 0;
33+
const TemplateFull: StoryFn<typeof ChatContent> = (args) => <ChatContent {...args} key={update++} />;
3334

3435
export const Default = TemplateFull.bind({});
3536
Default.args = {
@@ -44,6 +45,7 @@ Default.args = {
4445
<strong>Username</strong> 25 minutes ago
4546
</OverflowText>
4647
),
48+
onToggleSize: undefined,
4749
};
4850

4951
export const LongChatBubble = TemplateFull.bind({});

yarn.lock

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3373,6 +3373,13 @@
33733373
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.26.tgz#fa7891ba70fd39ddbaa7e85b6ff9175bb546bc1b"
33743374
integrity sha512-Z+2VcYXJwOqQ79HreLU/1fyQ88eXSSFh6I3JdrEHQIfYSI0kCQpTGvOrbE6jFGGYXKsHuwY9tBa/w5Uo6KzrEg==
33753375

3376+
"@types/react-is@^19.0.0":
3377+
version "19.0.0"
3378+
resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-19.0.0.tgz#eccf45556cf1858e9116eed1f9e7b51496501a7a"
3379+
integrity sha512-71dSZeeJ0t3aoPyY9x6i+JNSvg5m9EF2i2OlSZI5QoJuI8Ocgor610i+4A10TQmURR+0vLwcVCEYFpXdzM1Biw==
3380+
dependencies:
3381+
"@types/react" "*"
3382+
33763383
"@types/react-redux@^7.1.20":
33773384
version "7.1.34"
33783385
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.34.tgz#83613e1957c481521e6776beeac4fd506d11bd0e"
@@ -10127,7 +10134,7 @@ react-docgen@^7.0.0:
1012710134
resolve "^1.22.1"
1012810135
strip-indent "^4.0.0"
1012910136

10130-
react-dom@^16.13.1:
10137+
react-dom@^16.14.0:
1013110138
version "16.14.0"
1013210139
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
1013310140
integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==

0 commit comments

Comments
 (0)