11import React from "react" ;
2+ import { renderToString } from "react-dom/server" ;
3+ import * as ReactIs from "react-is" ;
24
35import { TestableComponent } from "../../components/interfaces" ;
46import { CLASSPREFIX as eccgui } from "../../configuration/constants" ;
57
68import { Markdown , MarkdownProps } from "./../../cmem/markdown/Markdown" ;
79import { DepictionProps } from "./../Depiction/Depiction" ;
810import { FlexibleLayoutContainer , FlexibleLayoutItem } from "./../FlexibleLayout" ;
11+ import { IconButton } from "./../Icon/IconButton" ;
912import { Spacing } from "./../Separation/Spacing" ;
10- import { HtmlContentBlock , OverflowTextProps } from "./../Typography" ;
13+ import { HtmlContentBlock , OverflowText , OverflowTextProps } from "./../Typography" ;
1114
1215export 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 ) ;
0 commit comments