Add data-layout to MELS for better CSS structure

This commit is contained in:
Germain Souquet 2021-07-27 11:30:25 +02:00
parent 788abac74d
commit 8104ba936c
4 changed files with 105 additions and 90 deletions

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
.mx_EventTile[data-layout=bubble], .mx_EventTile[data-layout=bubble],
.mx_EventTile[data-layout=bubble] ~ .mx_EventListSummary { .mx_EventListSummary[data-layout=bubble] {
--avatarSize: 32px; --avatarSize: 32px;
--gutterSize: 11px; --gutterSize: 11px;
--cornerRadius: 12px; --cornerRadius: 12px;
@ -224,90 +224,6 @@ limitations under the License.
border-left-color: $eventbubble-reply-color; border-left-color: $eventbubble-reply-color;
} }
&.mx_EventTile_bubbleContainer,
&.mx_EventTile_info,
& ~ .mx_EventListSummary[data-expanded=false] {
--backgroundColor: transparent;
--gutterSize: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 5px 0;
.mx_EventTile_avatar {
position: static;
order: -1;
margin-right: 5px;
}
}
& ~ .mx_EventListSummary {
--maxWidth: 80%;
margin-left: calc(var(--avatarSize) + var(--gutterSize));
margin-right: calc(var(--gutterSize) + var(--avatarSize));
.mx_EventListSummary_toggle {
float: none;
margin: 0;
order: 9;
margin-left: 5px;
}
.mx_EventListSummary_avatars {
padding-top: 0;
}
&::after {
content: "";
clear: both;
}
.mx_EventTile {
margin: 0 6px;
}
.mx_EventTile_line {
margin: 0 5px;
> a {
left: auto;
right: 0;
transform: translateX(calc(100% + 5px));
}
}
.mx_MessageActionBar {
transform: translate3d(90%, 0, 0);
}
}
& ~ .mx_EventListSummary[data-expanded=false] {
padding: 0 34px;
}
/* events that do not require bubble layout */
& ~ .mx_EventListSummary,
&.mx_EventTile_bad {
.mx_EventTile_line {
background: transparent;
}
&:hover {
&::before {
background: transparent;
}
}
}
& + .mx_EventListSummary {
.mx_EventTile {
margin-top: 0;
padding: 2px 0;
}
}
.mx_EventListSummary_toggle {
margin-right: 55px;
}
/* Special layout scenario for "Unable To Decrypt (UTD)" events */ /* Special layout scenario for "Unable To Decrypt (UTD)" events */
&.mx_EventTile_bad > .mx_EventTile_line { &.mx_EventTile_bad > .mx_EventTile_line {
display: grid; display: grid;
@ -342,3 +258,78 @@ limitations under the License.
max-width: 100%; max-width: 100%;
} }
} }
.mx_EventTile.mx_EventTile_bubbleContainer[data-layout=bubble],
.mx_EventTile.mx_EventTile_info[data-layout=bubble],
.mx_EventListSummary[data-layout=bubble][data-expanded=false] {
--backgroundColor: transparent;
--gutterSize: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 5px 0;
.mx_EventTile_avatar {
position: static;
order: -1;
margin-right: 5px;
}
}
.mx_EventListSummary[data-layout=bubble] {
--maxWidth: 80%;
margin-left: calc(var(--avatarSize) + var(--gutterSize));
margin-right: calc(var(--gutterSize) + var(--avatarSize));
.mx_EventListSummary_toggle {
float: none;
margin: 0;
order: 9;
margin-left: 5px;
margin-right: 55px;
}
.mx_EventListSummary_avatars {
padding-top: 0;
}
&::after {
content: "";
clear: both;
}
.mx_EventTile {
margin: 0 6px;
padding: 2px 0;
}
.mx_EventTile_line {
margin: 0 5px;
> a {
left: auto;
right: 0;
transform: translateX(calc(100% + 5px));
}
}
.mx_MessageActionBar {
transform: translate3d(90%, 0, 0);
}
}
.mx_EventListSummary[data-expanded=false][data-layout=bubble] {
padding: 0 34px;
}
/* events that do not require bubble layout */
.mx_EventListSummary[data-layout=bubble],
.mx_EventTile.mx_EventTile_bad[data-layout=bubble] {
.mx_EventTile_line {
background: transparent;
}
&:hover {
&::before {
background: transparent;
}
}
}

View file

@ -618,7 +618,15 @@ export default class MessagePanel extends React.Component<IProps, IState> {
for (const Grouper of groupers) { for (const Grouper of groupers) {
if (Grouper.canStartGroup(this, mxEv)) { if (Grouper.canStartGroup(this, mxEv)) {
grouper = new Grouper(this, mxEv, prevEvent, lastShownEvent, nextEvent, nextTile); grouper = new Grouper(
this,
mxEv,
prevEvent,
lastShownEvent,
this.props.layout,
nextEvent,
nextTile,
);
} }
} }
if (!grouper) { if (!grouper) {
@ -981,6 +989,7 @@ abstract class BaseGrouper {
public readonly event: MatrixEvent, public readonly event: MatrixEvent,
public readonly prevEvent: MatrixEvent, public readonly prevEvent: MatrixEvent,
public readonly lastShownEvent: MatrixEvent, public readonly lastShownEvent: MatrixEvent,
protected readonly layout: Layout,
public readonly nextEvent?: MatrixEvent, public readonly nextEvent?: MatrixEvent,
public readonly nextEventTile?: MatrixEvent, public readonly nextEventTile?: MatrixEvent,
) { ) {
@ -1107,6 +1116,7 @@ class CreationGrouper extends BaseGrouper {
onToggle={panel.onHeightChanged} // Update scroll state onToggle={panel.onHeightChanged} // Update scroll state
summaryMembers={[ev.sender]} summaryMembers={[ev.sender]}
summaryText={summaryText} summaryText={summaryText}
layout={this.layout}
> >
{ eventTiles } { eventTiles }
</EventListSummary>, </EventListSummary>,
@ -1134,10 +1144,11 @@ class RedactionGrouper extends BaseGrouper {
ev: MatrixEvent, ev: MatrixEvent,
prevEvent: MatrixEvent, prevEvent: MatrixEvent,
lastShownEvent: MatrixEvent, lastShownEvent: MatrixEvent,
layout: Layout,
nextEvent: MatrixEvent, nextEvent: MatrixEvent,
nextEventTile: MatrixEvent, nextEventTile: MatrixEvent,
) { ) {
super(panel, ev, prevEvent, lastShownEvent, nextEvent, nextEventTile); super(panel, ev, prevEvent, lastShownEvent, layout, nextEvent, nextEventTile);
this.events = [ev]; this.events = [ev];
} }
@ -1202,6 +1213,7 @@ class RedactionGrouper extends BaseGrouper {
onToggle={panel.onHeightChanged} // Update scroll state onToggle={panel.onHeightChanged} // Update scroll state
summaryMembers={Array.from(senders)} summaryMembers={Array.from(senders)}
summaryText={_t("%(count)s messages deleted.", { count: eventTiles.length })} summaryText={_t("%(count)s messages deleted.", { count: eventTiles.length })}
layout={this.layout}
> >
{ eventTiles } { eventTiles }
</EventListSummary>, </EventListSummary>,
@ -1230,8 +1242,9 @@ class MemberGrouper extends BaseGrouper {
public readonly event: MatrixEvent, public readonly event: MatrixEvent,
public readonly prevEvent: MatrixEvent, public readonly prevEvent: MatrixEvent,
public readonly lastShownEvent: MatrixEvent, public readonly lastShownEvent: MatrixEvent,
protected readonly layout: Layout,
) { ) {
super(panel, event, prevEvent, lastShownEvent); super(panel, event, prevEvent, lastShownEvent, layout);
this.events = [event]; this.events = [event];
} }
@ -1306,6 +1319,7 @@ class MemberGrouper extends BaseGrouper {
events={this.events} events={this.events}
onToggle={panel.onHeightChanged} // Update scroll state onToggle={panel.onHeightChanged} // Update scroll state
startExpanded={highlightInMels} startExpanded={highlightInMels}
layout={this.layout}
> >
{ eventTiles } { eventTiles }
</MemberEventListSummary>, </MemberEventListSummary>,

View file

@ -22,6 +22,7 @@ import MemberAvatar from '../avatars/MemberAvatar';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { useStateToggle } from "../../../hooks/useStateToggle"; import { useStateToggle } from "../../../hooks/useStateToggle";
import AccessibleButton from "./AccessibleButton"; import AccessibleButton from "./AccessibleButton";
import { Layout } from '../../../settings/Layout';
interface IProps { interface IProps {
// An array of member events to summarise // An array of member events to summarise
@ -38,6 +39,8 @@ interface IProps {
children: ReactNode[]; children: ReactNode[];
// Called when the event list expansion is toggled // Called when the event list expansion is toggled
onToggle?(): void; onToggle?(): void;
// The layout currently used
layout?: Layout;
} }
const EventListSummary: React.FC<IProps> = ({ const EventListSummary: React.FC<IProps> = ({
@ -48,6 +51,7 @@ const EventListSummary: React.FC<IProps> = ({
startExpanded, startExpanded,
summaryMembers = [], summaryMembers = [],
summaryText, summaryText,
layout,
}) => { }) => {
const [expanded, toggleExpanded] = useStateToggle(startExpanded); const [expanded, toggleExpanded] = useStateToggle(startExpanded);
@ -63,7 +67,7 @@ const EventListSummary: React.FC<IProps> = ({
// If we are only given few events then just pass them through // If we are only given few events then just pass them through
if (events.length < threshold) { if (events.length < threshold) {
return ( return (
<li className="mx_EventListSummary" data-scroll-tokens={eventIds} data-expanded={true}> <li className="mx_EventListSummary" data-scroll-tokens={eventIds} data-expanded={true} data-layout={layout}>
{ children } { children }
</li> </li>
); );
@ -92,7 +96,7 @@ const EventListSummary: React.FC<IProps> = ({
} }
return ( return (
<li className="mx_EventListSummary" data-scroll-tokens={eventIds} data-expanded={expanded + ""}> <li className="mx_EventListSummary" data-scroll-tokens={eventIds} data-expanded={expanded + ""} data-layout={layout}>
<AccessibleButton className="mx_EventListSummary_toggle" onClick={toggleExpanded} aria-expanded={expanded}> <AccessibleButton className="mx_EventListSummary_toggle" onClick={toggleExpanded} aria-expanded={expanded}>
{ expanded ? _t('collapse') : _t('expand') } { expanded ? _t('collapse') : _t('expand') }
</AccessibleButton> </AccessibleButton>
@ -103,6 +107,7 @@ const EventListSummary: React.FC<IProps> = ({
EventListSummary.defaultProps = { EventListSummary.defaultProps = {
startExpanded: false, startExpanded: false,
layout: Layout.Group,
}; };
export default EventListSummary; export default EventListSummary;

View file

@ -25,12 +25,15 @@ import { formatCommaSeparatedList } from '../../../utils/FormattingUtils';
import { isValid3pidInvite } from "../../../RoomInvite"; import { isValid3pidInvite } from "../../../RoomInvite";
import EventListSummary from "./EventListSummary"; import EventListSummary from "./EventListSummary";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { Layout } from '../../../settings/Layout';
interface IProps extends Omit<ComponentProps<typeof EventListSummary>, "summaryText" | "summaryMembers"> { interface IProps extends Omit<ComponentProps<typeof EventListSummary>, "summaryText" | "summaryMembers"> {
// The maximum number of names to show in either each summary e.g. 2 would result "A, B and 234 others left" // The maximum number of names to show in either each summary e.g. 2 would result "A, B and 234 others left"
summaryLength?: number; summaryLength?: number;
// The maximum number of avatars to display in the summary // The maximum number of avatars to display in the summary
avatarsMaxLength?: number; avatarsMaxLength?: number;
// The currently selected layout
layout: Layout;
} }
interface IUserEvents { interface IUserEvents {
@ -67,6 +70,7 @@ export default class MemberEventListSummary extends React.Component<IProps> {
summaryLength: 1, summaryLength: 1,
threshold: 3, threshold: 3,
avatarsMaxLength: 5, avatarsMaxLength: 5,
layout: Layout.Group,
}; };
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
@ -453,6 +457,7 @@ export default class MemberEventListSummary extends React.Component<IProps> {
startExpanded={this.props.startExpanded} startExpanded={this.props.startExpanded}
children={this.props.children} children={this.props.children}
summaryMembers={[...latestUserAvatarMember.values()]} summaryMembers={[...latestUserAvatarMember.values()]}
layout={this.props.layout}
summaryText={this.generateSummary(aggregate.names, orderedTransitionSequences)} />; summaryText={this.generateSummary(aggregate.names, orderedTransitionSequences)} />;
} }
} }