Skip to content
This repository was archived by the owner on Oct 19, 2023. It is now read-only.
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
15 changes: 8 additions & 7 deletions .browserslistrc
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
> 1%
ie >= 8
edge >= 15
edge >= 16
ie_mob >= 10
ff >= 45
chrome >= 45
safari >= 7
opera >= 23
ios >= 7
android >= 4
ff >= 52
chrome >= 57
safari >= 10.1
opera >= 44
ios >= 10.3
and_uc >= 12.12
and_chr >= 97
bb >= 10
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## Release 2.0.4 - Unreleased
### Fixed
* BubbleChat: Use bottom alignment for avatar
* BubbleChat.Image: Set `lineHeight` to `0` to make image in the same baseline with avatar

## Release 2.0.3 - January 18, 2022
### Fixed
* Code base: use absolute imports, rearrange hooks and utils
Expand Down
2 changes: 1 addition & 1 deletion src/components/BubbleChat/Image.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const BubbleChatImage = React.forwardRef(({ className, ...props }, ref) => {
ref={ref}
className={classNames(
'BubbleChat-image',
'u-marginBottomTiny',
'u-lineHeightNone',
(type === 'inbound') && 'u-textRight',
className && className
)}
Expand Down
261 changes: 145 additions & 116 deletions src/components/BubbleChat/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const typeThemeClassNames = {

const typeRadiusClassNames = {
inbound: 'u-roundedExtraLarge u-roundedBottomRightNone',
outbound: 'u-roundedExtraLarge u-roundedBottomLeftNone',
outbound: 'u-roundedExtraLarge u-roundedBottomLeftNone',
system: 'u-roundedExtraLarge',
};

Expand All @@ -105,7 +105,65 @@ const BubbleChat = React.forwardRef(({ className, isTyping, text, type, variant,
else if (type === 'outbound') variantOri = 'light';
else if (type === 'system') variantOri = 'primaryLight';
}

const context = useMemo(() => ({ type }), [type]);

const renderTime = () => (
<>
{/* Empty <div /> to occupy the bottom left corner of grid template */}
{type !== 'inbound' && <div />}
<div
className={classNames(
'BubbleChat-time',
'u-text100 u-textLight u-marginTopTiny',
(type === 'inbound' && children) && 'u-textRight'
)}
style={{
...(type === 'inbound' ? { justifySelf: 'flex-end' } : {}),
}}
>
{time}
</div>
{/* Empty <div /> to occupy the bottom right corner of grid template */}
{type === 'inbound' && <div />}
</>
);

const renderTyping = () => (
<div className={classNames(
'u-overflowHidden',
type && typeRadiusClassNames[type]
)}
>
<div
className={classNames(
'BubbleChat-typing',
'u-paddingExtraSmall u-positionRelative',
type && typeThemeClassNames[type],
variantOri && variantTextClassNames[variantOri],
variantOri && variantClassNames[variantOri],
)}
>
<div className="BubbleChat-typingContext u-positionRelative u-fontSizeNone" style={{ width: 32, height: 10 }} />
</div>
</div>
);

const renderAvatar = (type = 'outbound') => (
<div className={
classNames(
type === 'outbound' ? 'u-marginRightExtraSmall' : 'u-marginLeftExtraSmall',
'u-alignSelfEnd',
)
}
>
{typeof (avatar) === 'function'
? avatar()
: <Avatar name={avatar} size="small" />
}
</div>
);

return (
<Context.Provider value={context}>
<div
Expand All @@ -118,139 +176,110 @@ const BubbleChat = React.forwardRef(({ className, isTyping, text, type, variant,
className && className
)}
>
<div className={classNames(
'BubbleChat-container',
'u-flex',
(type === 'inbound') && 'u-justifyContentEnd u-marginLeftAuto',
)}
>
{(avatar && type !== 'inbound') && (
<div className="u-flexShrink0 u-marginRightExtraSmall u-marginTopExtraSmall">
{
typeof (avatar) === 'function' ? avatar() : <Avatar name={avatar} size="small" />
}
</div>
<div
className={classNames(
'BubbleChat-container',
(type === 'inbound') && 'u-justifyContentEnd u-marginLeftAuto',
)}
style={{
display: 'grid',
gridTemplateColumns: type === 'inbound' ? '1fr auto' : 'auto 1fr',
}}
>
{(type !== 'inbound' && avatar) && renderAvatar('outbound')}
{/* Empty <div /> to occupy the top left corner of grid template */}
{(type !== 'inbound' && !avatar) && <div />}

<div className={classNames(
'BubbleChat-context',
'u-flex u-flexColumn',
children && 'u-widthFull',
children && 'u-maxWidthFull',
(type === 'inbound' && !avatar) && 'u-marginLeftMedium',
(type === 'inbound' && !children) && 'u-alignItemsEnd'
)}
>
<React.Fragment>
{isTyping && (
<div className={classNames(
'u-overflowHidden',
type && typeRadiusClassNames[type]
)}
>
<div
className={classNames(
'BubbleChat-typing',
'u-paddingExtraSmall u-positionRelative',
type && typeThemeClassNames[type],
variantOri && variantTextClassNames[variantOri],
variantOri && variantClassNames[variantOri],
)}
>
<div className="BubbleChat-typingContext u-positionRelative u-fontSizeNone" style={{ width: 32, height: 10 }} />
</div>
</div>
)}
{isTyping && renderTyping()}
{!isTyping && (
<React.Fragment>
{children || (
<div className="u-positionRelative">
{actionBar && (
<div className={classNames(
'u-positionAbsolute u-positionTop u-marginTopTiny u-marginHorizontalExtraSmall',
type === 'inbound' ? 'u-positionRight-100' : 'u-positionLeft-100',
actionBarClassName && actionBarClassName,
)}
>
{actionBar}
</div>
)}
<div className={classNames(
'u-overflowHidden u-flexInline u-flexColumn',
type && typeRadiusClassNames[type]
)}
>
<div
className={classNames(
'BubbleChat-text',
'u-paddingVerticalExtraSmall u-paddingHorizontalSmall u-textPreLine',
type && typeThemeClassNames[type],
((variantOri === 'primary' || variantOri === 'dark' || variantOri === 'transparentDark') && textClassName) ? textClassName : variantTextClassNames[variantOri],
variantOri && variantClassNames[variantOri],

<React.Fragment>
{children || (
<div className="u-positionRelative">
{actionBar && (
<div className={classNames(
'u-positionAbsolute u-positionTop u-marginTopTiny u-marginHorizontalExtraSmall',
type === 'inbound' ? 'u-positionRight-100' : 'u-positionLeft-100',
actionBarClassName && actionBarClassName,
)}
>
{actionBar}
</div>
)}
onClick={onClickText}
>
{text}
</div>
{options && (
<div className="u-flex u-flexColumn u-border u-borderUltraLight u-roundedBottomExtraLarge u-text200 u-overflowHidden">
{options.map((option, idx) => {
let cn;
let handleClick;
<div className={classNames(
'u-overflowHidden u-flexInline u-flexColumn',
type && typeRadiusClassNames[type]
)}
>
<div
className={classNames(
'BubbleChat-text',
'u-paddingVerticalExtraSmall u-paddingHorizontalSmall u-textPreLine',
type && typeThemeClassNames[type],
((variantOri === 'primary' || variantOri === 'dark' || variantOri === 'transparentDark') && textClassName) ? textClassName : variantTextClassNames[variantOri],
variantOri && variantClassNames[variantOri],

)}
onClick={onClickText}
>
{text}
</div>
{options && (
<div className="u-flex u-flexColumn u-border u-borderUltraLight u-roundedBottomExtraLarge u-text200 u-overflowHidden">
{options.map((option, idx) => {
let cn;
let handleClick;

if (option.id === currentOption) {
cn = `u-backgroundPrimary ${textClassName || 'u-textWhite'} ${disabledOption ? 'u-cursorNotAllow' : ''}`;
handleClick = null;
} else if (disabledOption) {
cn = 'u-backgroundLighter u-textGray u-cursorNotAllow';
handleClick = null;
} else {
cn = 'u-backgroundWhite hover:u-backgroundLightest u-textPrimary u-cursorPointer';
handleClick = () => onSelectOption(option.id);
}
if (option.id === currentOption) {
cn = `u-backgroundPrimary ${textClassName || 'u-textWhite'} ${disabledOption ? 'u-cursorNotAllow' : ''}`;
handleClick = null;
} else if (disabledOption) {
cn = 'u-backgroundLighter u-textGray u-cursorNotAllow';
handleClick = null;
} else {
cn = 'u-backgroundWhite hover:u-backgroundLightest u-textPrimary u-cursorPointer';
handleClick = () => onSelectOption(option.id);
}

return (
<button
key={option.id}
type="button"
disabled={disabledOption}
onClick={handleClick}
className={classNames(
'u-paddingExtraSmall u-transitionColors u-easeInOut u-duration150 u-textCenter',
(idx !== 0) ? 'u-borderTop u-borderBottomNone u-borderLeftNone u-borderRightNone u-borderUltraLight' : 'u-borderNone',
cn,
)}
>
{option.name}
</button>
);
})}
return (
<button
key={option.id}
type="button"
disabled={disabledOption}
onClick={handleClick}
className={classNames(
'u-paddingExtraSmall u-transitionColors u-easeInOut u-duration150 u-textCenter',
(idx !== 0) ? 'u-borderTop u-borderBottomNone u-borderLeftNone u-borderRightNone u-borderUltraLight' : 'u-borderNone',
cn,
)}
>
{option.name}
</button>
);
})}
</div>
)}
</div>
</div>
)}
</div>
</div>
)}
{time && (
<div className={classNames(
'BubbleChat-time',
'u-text100 u-textLight u-marginTopTiny',
(type === 'inbound' && children) && 'u-textRight'
)}
>
{time}
</div>
)}
</React.Fragment>
)}
</React.Fragment>
)}
</React.Fragment>
</div>
{(avatar && (type !== 'outbound' && type !== 'system')) && (
<div className="u-flexShrink0 u-marginLeftExtraSmall u-marginTopExtraSmall">
{
typeof (avatar) === 'function' ? avatar() : <Avatar name={avatar} size="small" />
}
</div>
)}

{((type !== 'outbound' && type !== 'system') && avatar) && renderAvatar('inbound')}
{/* Empty <div /> to occupy the top right corner of grid template */}
{((type !== 'outbound' && type !== 'system') && !avatar) && <div />}

{time && renderTime()}
</div>
</div>
</Context.Provider>
Expand Down