1
0
mirror of https://github.com/element-hq/element-web.git synced 2026-10-24 23:19:26 +03:00
Files
element-web/apps/web/res/css/structures/_NewTimelinePanel.pcss
David Langley 26771fd3b1 New timeline panel in the room view (labs) (#34859)
* Fix virtual-core scroll compensation during in-flight scrolls

Patch fixes so the timeline holds its place when rows change size while a
scroll is still settling: decisions use the up-to-date scroll position, the
stay-at-bottom correction retries if the browser clamped it, and corrections
now also apply while scrolling upwards. All candidates for upstreaming.

* Use the timeline's own 4px tolerance when deciding it is at the bottom

* Fix full-width timeline rows overflowing the right-hand edge

The list kept the browser's default 40px indent, which pushed every row 40px
too wide. Only full-width rows (the read marker line, date separators) showed
it.

* Add a labelled "New" style to the read marker

The new design: a green line with a "New" label at its right-hand end. Opt-in
via a new label prop, so the legacy timeline keeps its plain line. Also adds an
as="div" option for callers that already provide the list item.

* Fix the reserved image box collapsing while the image loads

The box meant to hold a photo's space before it downloads was being sized to
zero by the browser, so the message grew when the photo arrived and shoved the
timeline. Failed images also keep the reserved height now instead of collapsing
to one line.

* Fix the timeline stalling at the top with more history available

A request to load more history that arrived while a fetch was already running
was silently dropped, and the view would sometimes never re-ask — leaving the
timeline stuck at the top until the user jiggled the scroll. Such requests are
now remembered and run when the current fetch finishes, matching what the old
ScrollPanel did.

* Render polls at full size straight away

The question and options are in the event we already have; only votes need
fetching. Rendering them immediately stops a freshly loaded poll growing ~180px
when its votes arrive and shoving the timeline.

* Add a compact reply preview that keeps one height while it loads

For the new timeline: the quoted message shows immediately when it is already
loaded, a fixed-height skeleton stands in while one is fetched, and the rows are
pinned to one height so the preview never resizes and shoves the messages around
it. Opt-in via a new EventTile prop; the legacy timeline is unchanged.

* Add the new timeline to the room view behind a labs flag

The new virtualised timeline panel, rendering events through the legacy
EventTile for now. Enabled by the "Improved timeline scrolling and navigation"
labs setting; the existing timeline is untouched when the flag is off.

* Address SonarCloud findings

Mark component props read-only, hoist the timeline row renderer to module
level, pass the quoted event into the reply header fetch instead of re-reading
state after the await, and drop a TODO tag.

* Fix image overflow in narrow panes, and tidy up timeline rows

The clamp stopping a reserved image box from overflowing its container lived in
new-timeline CSS, but the box is drawn by a shared component the legacy timeline
and thread panel use too, so images could overflow there. Moved it onto the
component's own link wrapper, beside the width that makes it necessary: a 757px
image in a 350px pane rendered at 757px before and 350px after, and still takes
its natural size when there is room.

Also, for the new timeline's rows: skip a row whose event has gone from the room
rather than letting the tile crash and lose the whole timeline, drop a leftover
placeholder that drew the word "Gap", and give the pagination spinner a label
screen readers can announce.

* Add vis baseline and padding for the labelled read marker story

The story had no left/right inset, so the label rendered flush against the edge
of the frame and was clipped. Pads it like the timeline does, and adds the
baseline image the visual test needs.

* Add tests for the new timeline panel, tile adapter and reply preview

Covers how the panel draws each kind of row — messages, the read marker, date
separators, the loading spinner, gaps — along with the layout fallback, edit
state scoping, and skipping a row whose event has gone. Also covers the tile
adapter's pass-through and the reply preview's three states (already loaded,
skeleton while fetching, and error).

The panel's tests stand in a stub for the virtualised TimelineView, which needs
real layout the test environment doesn't have and has its own tests.

* Show the read marker properly in its stories

The stories dropped the marker into a plain list with no room around it, so the
browser's list styling came along for the ride: every baseline had a bullet in
it, and the rule was pressed against the top edge of the frame, hard to see and
liable to be clipped by the marker's own 1px offset. The hidden-marker story was
a picture of a bullet and nothing else.

Resetting the list and insetting the stories draws the marker the way a timeline
does — a hairline, evenly inset, with room above it.

* Update the bubble media baseline for the reserved-size fix

The second media box in that story was being shrunk to fit its content (255px
wide) rather than taking the size the event declares; with the reserved box now
holding, it renders at 297px and its spinner and timestamp sit inside it instead
of overflowing. Every other story in the timeline suite is unchanged.

* Create new-timeline.spec.ts

* Move panel CSS to mx_NewTimelinePanel and use new DateSeperator

* fix tanstack virtial patch and remove reply clamping to 1 line

clamping the reply to one line is awkward and not real needed now that above the fold compensation works. it will visually expand when on screen.

One of our tanstack fixes was fixed upstreamed so can be removed.

* Update media---bubble---default-auto.png
2026-09-16 21:42:40 +00:00

66 lines
2.7 KiB
Plaintext

/*
Copyright 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
/*
* The new timeline's reply-preview rules — the loading skeleton and the row
* heights that keep it height-neutral — live alongside the reply components in
* _ReplyChain.pcss / _ReplyTile.pcss.
*/
.mx_NewTimelinePanel {
/* Fill the room view body, like the old message panel does. */
height: 100%;
/* Kept mounted but invisible, e.g. while search results are shown, so the
timeline keeps its scroll position and loaded window. */
&.mx_NewTimelinePanel_hidden {
display: none;
}
/* The virtualized timeline renders its rows as an <ol role="list"> of <li>
tiles (mx_TimelineView_list / mx_TimelineView_tile in TimelineView; the <ol>
and <li> reset their own list markers inline). Inset the tile content to
match the legacy mx_RoomView_MessageList padding so messages don't sit flush
against the panel edges.
The inset goes on the tiles rather than the <ol>: the rows are absolutely
positioned, so their containing block is the <ol>'s padding box and padding
there would not inset them. box-sizing + padding-inline on each row does,
and — being horizontal only — leaves the virtualizer's measured row heights
and vertical offsets untouched. */
.mx_TimelineView_tile {
box-sizing: border-box;
padding-inline: var(--RoomView_MessageList-padding);
}
/* The row holding the spinner shown while more history is fetched. Its height is
fixed so the row always measures the same. */
.mx_NewTimelinePanel_loading {
display: flex;
align-items: center;
justify-content: center;
height: 32px;
overflow: hidden;
}
/* An image that fails to load replaces its reserved media box with a short
error message, which would otherwise pull every row below it upwards. Hold
the row at the height of the box being replaced — ImageBodyView passes it
as --mx-image-reserved-height, and only for images whose event declared
dimensions; anything else reserved no box, so it gets no minimum.
The error state sets `display: block`, so laying the message out needs flex
re-asserting here; the root's `flex-direction: column` then means the cross
axis is horizontal, and centring in the box is align-items (horizontally)
plus justify-content (vertically). */
span.mx_ImageBody {
display: flex;
align-items: center;
justify-content: center;
min-height: var(--mx-image-reserved-height, 0);
}
}