Merge pull request #2515 from matrix-org/bwindels/roomlistjslayout-bisbis

Redesign: switch layout when filtering room sublists
This commit is contained in:
Bruno Windels 2019-01-29 09:32:32 +00:00 committed by GitHub
commit 012e358147
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 80 additions and 27 deletions

View file

@ -84,7 +84,7 @@ const RoomSubList = React.createClass({
// The dataset elements are added in the RoomList _initAndPositionStickyHeaders method // The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
isCollapsableOnClick: function() { isCollapsableOnClick: function() {
const stuck = this.refs.header.dataset.stuck; const stuck = this.refs.header.dataset.stuck;
if (this.state.hidden || stuck === undefined || stuck === "none") { if (!this.props.forceExpand && (this.state.hidden || stuck === undefined || stuck === "none")) {
return true; return true;
} else { } else {
return false; return false;
@ -238,7 +238,7 @@ const RoomSubList = React.createClass({
} }
}, },
_getHeaderJsx: function() { _getHeaderJsx: function(isCollapsed) {
const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
const subListNotifications = this.roomNotificationCount(); const subListNotifications = this.roomNotificationCount();
const subListNotifCount = subListNotifications[0]; const subListNotifCount = subListNotifications[0];
@ -287,8 +287,8 @@ const RoomSubList = React.createClass({
if (len) { if (len) {
const chevronClasses = classNames({ const chevronClasses = classNames({
'mx_RoomSubList_chevron': true, 'mx_RoomSubList_chevron': true,
'mx_RoomSubList_chevronRight': this.state.hidden, 'mx_RoomSubList_chevronRight': isCollapsed,
'mx_RoomSubList_chevronDown': !this.state.hidden, 'mx_RoomSubList_chevronDown': !isCollapsed,
}); });
chevron = (<div className={chevronClasses}></div>); chevron = (<div className={chevronClasses}></div>);
} }
@ -321,21 +321,23 @@ const RoomSubList = React.createClass({
render: function() { render: function() {
const len = this.props.list.length + this.props.extraTiles.length; const len = this.props.list.length + this.props.extraTiles.length;
const isCollapsed = this.state.hidden && !this.props.forceExpand;
if (len) { if (len) {
const subListClasses = classNames({ const subListClasses = classNames({
"mx_RoomSubList": true, "mx_RoomSubList": true,
"mx_RoomSubList_hidden": this.state.hidden, "mx_RoomSubList_hidden": isCollapsed,
"mx_RoomSubList_nonEmpty": len && !this.state.hidden, "mx_RoomSubList_nonEmpty": len && !isCollapsed,
}); });
if (this.state.hidden) {
if (isCollapsed) {
return <div ref="subList" className={subListClasses}> return <div ref="subList" className={subListClasses}>
{this._getHeaderJsx()} {this._getHeaderJsx(isCollapsed)}
</div>; </div>;
} else { } else {
const tiles = this.makeRoomTiles(); const tiles = this.makeRoomTiles();
tiles.push(...this.props.extraTiles); tiles.push(...this.props.extraTiles);
return <div ref="subList" className={subListClasses}> return <div ref="subList" className={subListClasses}>
{this._getHeaderJsx()} {this._getHeaderJsx(isCollapsed)}
<IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll"> <IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll">
{ tiles } { tiles }
</IndicatorScrollbar> </IndicatorScrollbar>
@ -344,13 +346,13 @@ const RoomSubList = React.createClass({
} else { } else {
const Loader = sdk.getComponent("elements.Spinner"); const Loader = sdk.getComponent("elements.Spinner");
let content; let content;
if (this.props.showSpinner && !this.state.hidden) { if (this.props.showSpinner && !isCollapsed) {
content = <Loader />; content = <Loader />;
} }
return ( return (
<div ref="subList" className="mx_RoomSubList"> <div ref="subList" className="mx_RoomSubList">
{ this._getHeaderJsx() } { this._getHeaderJsx(isCollapsed) }
{ content } { content }
</div> </div>
); );

View file

@ -82,7 +82,11 @@ module.exports = React.createClass({
this.collapsedState = collapsedJson ? JSON.parse(collapsedJson) : {}; this.collapsedState = collapsedJson ? JSON.parse(collapsedJson) : {};
this._layoutSections = []; this._layoutSections = [];
this._layout = new Layout((key, size) => { const unfilteredOptions = {
allowWhitespace: true,
handleHeight: 1,
};
this._unfilteredlayout = new Layout((key, size) => {
const subList = this._subListRefs[key]; const subList = this._subListRefs[key];
if (subList) { if (subList) {
subList.setHeight(size); subList.setHeight(size);
@ -95,7 +99,19 @@ module.exports = React.createClass({
window.localStorage.setItem("mx_roomlist_sizes", window.localStorage.setItem("mx_roomlist_sizes",
JSON.stringify(this.subListSizes)); JSON.stringify(this.subListSizes));
} }
}, this.subListSizes, this.collapsedState); }, this.subListSizes, this.collapsedState, unfilteredOptions);
this._filteredLayout = new Layout((key, size) => {
const subList = this._subListRefs[key];
if (subList) {
subList.setHeight(size);
}
}, null, null, {
allowWhitespace: false,
handleHeight: 0,
});
this._layout = this._unfilteredlayout;
return { return {
isLoadingLeftRooms: false, isLoadingLeftRooms: false,
@ -187,15 +203,21 @@ module.exports = React.createClass({
}, },
componentDidUpdate: function(prevProps) { componentDidUpdate: function(prevProps) {
let forceLayoutUpdate = false;
this._repositionIncomingCallBox(undefined, false); this._repositionIncomingCallBox(undefined, false);
// if (this.props.searchFilter !== prevProps.searchFilter) { if (!this.props.searchFilter && prevProps.searchFilter) {
// this._checkSubListsOverflow(); this._layout = this._unfilteredlayout;
// } forceLayoutUpdate = true;
} else if (this.props.searchFilter && !prevProps.searchFilter) {
this._layout = this._filteredLayout;
forceLayoutUpdate = true;
}
this._layout.update( this._layout.update(
this._layoutSections, this._layoutSections,
this.resizeContainer && this.resizeContainer.clientHeight, this.resizeContainer && this.resizeContainer.clientHeight,
forceLayoutUpdate,
); );
// TODO: call layout.setAvailableHeight, window height was changed when bannerShown prop was changed this._checkSubListsOverflow();
}, },
onAction: function(payload) { onAction: function(payload) {
@ -617,7 +639,7 @@ module.exports = React.createClass({
onHeaderClick(collapsed); onHeaderClick(collapsed);
} }
}; };
const startAsHidden = props.startAsHidden || this.collapsedState[chosenKey]; let startAsHidden = props.startAsHidden || this.collapsedState[chosenKey];
this._layoutSections.push({ this._layoutSections.push({
id: chosenKey, id: chosenKey,
count: len, count: len,
@ -625,6 +647,7 @@ module.exports = React.createClass({
let subList = (<RoomSubList let subList = (<RoomSubList
ref={this._subListRef.bind(this, chosenKey)} ref={this._subListRef.bind(this, chosenKey)}
startAsHidden={startAsHidden} startAsHidden={startAsHidden}
forceExpand={!!this.props.searchFilter}
onHeaderClick={onSubListHeaderClick} onHeaderClick={onSubListHeaderClick}
key={chosenKey} key={chosenKey}
label={label} label={label}

View file

@ -16,9 +16,6 @@ limitations under the License.
import FixedDistributor from "./fixed"; import FixedDistributor from "./fixed";
// const allowWhitespace = true;
const handleHeight = 1;
function clamp(height, min, max) { function clamp(height, min, max) {
if (height > max) return max; if (height > max) return max;
if (height < min) return min; if (height < min) return min;
@ -26,7 +23,7 @@ function clamp(height, min, max) {
} }
export class Layout { export class Layout {
constructor(applyHeight, initialSizes, collapsedState) { constructor(applyHeight, initialSizes, collapsedState, options) {
// callback to set height of section // callback to set height of section
this._applyHeight = applyHeight; this._applyHeight = applyHeight;
// list of {id, count} objects, // list of {id, count} objects,
@ -41,6 +38,17 @@ export class Layout {
this._sectionHeights = Object.assign({}, initialSizes); this._sectionHeights = Object.assign({}, initialSizes);
// in-progress heights, while dragging. Committed on mouse-up. // in-progress heights, while dragging. Committed on mouse-up.
this._heights = []; this._heights = [];
// use while manually resizing to cancel
// the resize for a given mouse position
// when the previous resize made the layout
// constrained
this._clampedOffset = 0;
// used while manually resizing, to clear
// _clampedOffset when the direction of resizing changes
this._lastOffset = 0;
this._allowWhitespace = options && options.allowWhitespace;
this._handleHeight = (options && options.handleHeight) || 0;
} }
setAvailableHeight(newSize) { setAvailableHeight(newSize) {
@ -60,7 +68,7 @@ export class Layout {
this._applyNewSize(); this._applyNewSize();
} }
update(sections, availableHeight) { update(sections, availableHeight, force = false) {
let heightChanged = false; let heightChanged = false;
if (Number.isFinite(availableHeight) && availableHeight !== this._availableHeight) { if (Number.isFinite(availableHeight) && availableHeight !== this._availableHeight) {
@ -75,7 +83,7 @@ export class Layout {
return a.id !== b.id || a.count !== b.count; return a.id !== b.id || a.count !== b.count;
}); });
if (!heightChanged && !sectionsChanged) { if (!heightChanged && !sectionsChanged && !force) {
return; return;
} }
@ -104,7 +112,7 @@ export class Layout {
const collapsed = this._collapsedState[section.id]; const collapsed = this._collapsedState[section.id];
return count + (collapsed ? 0 : 1); return count + (collapsed ? 0 : 1);
}, 0); }, 0);
return this._availableHeight - ((nonCollapsedSectionCount - 1) * handleHeight); return this._availableHeight - ((nonCollapsedSectionCount - 1) * this._handleHeight);
} }
_applyNewSize() { _applyNewSize() {
@ -130,9 +138,10 @@ export class Layout {
if (collapsed) { if (collapsed) {
return this._sectionHeight(0); return this._sectionHeight(0);
} else if (!this._allowWhitespace) {
return this._sectionHeight(section.count);
} else { } else {
return 100000; return 100000;
// return this._sectionHeight(section.count);
} }
} }
@ -268,6 +277,22 @@ export class Layout {
this._sectionHeights[section.id] = this._heights[i]; this._sectionHeights[section.id] = this._heights[i];
}); });
} }
_setUncommittedSectionHeight(sectionIndex, offset) {
if (Math.sign(offset) != Math.sign(this._lastOffset)) {
this._clampedOffset = undefined;
}
if (this._clampedOffset !== undefined) {
if (offset < 0 && offset < this._clampedOffset) {
return;
}
if (offset > 0 && offset > this._clampedOffset) {
return;
}
}
this._clampedOffset = this._relayout(sectionIndex, offset);
this._lastOffset = offset;
}
} }
class Handle { class Handle {
@ -278,7 +303,10 @@ class Handle {
} }
setHeight(height) { setHeight(height) {
this._layout._relayout(this._sectionIndex, height - this._initialHeight); this._layout._setUncommittedSectionHeight(
this._sectionIndex,
height - this._initialHeight,
);
return this; return this;
} }