From 9f362e488c55c680320bb70696771630ecd7a859 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 21 Jan 2016 11:30:37 +0000 Subject: [PATCH 1/4] Add a TruncatedList component, which truncates children passed to it. --- src/component-index.js | 6 +- .../views/elements/TruncatedList.js | 61 +++++++++++++++++++ src/components/views/rooms/MemberList.js | 5 +- 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 src/components/views/elements/TruncatedList.js diff --git a/src/component-index.js b/src/component-index.js index 2446b26b8d..50803c045e 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -23,6 +23,7 @@ limitations under the License. module.exports.components = {}; module.exports.components['structures.CreateRoom'] = require('./components/structures/CreateRoom'); +module.exports.components['structures.login.ForgotPassword'] = require('./components/structures/login/ForgotPassword'); module.exports.components['structures.login.Login'] = require('./components/structures/login/Login'); module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration'); module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration'); @@ -31,10 +32,6 @@ module.exports.components['structures.RoomView'] = require('./components/structu module.exports.components['structures.ScrollPanel'] = require('./components/structures/ScrollPanel'); module.exports.components['structures.UploadBar'] = require('./components/structures/UploadBar'); module.exports.components['structures.UserSettings'] = require('./components/structures/UserSettings'); -module.exports.components['structures.login.ForgotPassword'] = require('./components/structures/login/ForgotPassword'); -module.exports.components['structures.login.Login'] = require('./components/structures/login/Login'); -module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration'); -module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration'); module.exports.components['views.avatars.BaseAvatar'] = require('./components/views/avatars/BaseAvatar'); module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar'); module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar'); @@ -49,6 +46,7 @@ module.exports.components['views.elements.EditableText'] = require('./components module.exports.components['views.elements.PowerSelector'] = require('./components/views/elements/PowerSelector'); module.exports.components['views.elements.ProgressBar'] = require('./components/views/elements/ProgressBar'); module.exports.components['views.elements.TintableSvg'] = require('./components/views/elements/TintableSvg'); +module.exports.components['views.elements.TruncatedList'] = require('./components/views/elements/TruncatedList'); module.exports.components['views.elements.UserSelector'] = require('./components/views/elements/UserSelector'); module.exports.components['views.login.CaptchaForm'] = require('./components/views/login/CaptchaForm'); module.exports.components['views.login.CasLogin'] = require('./components/views/login/CasLogin'); diff --git a/src/components/views/elements/TruncatedList.js b/src/components/views/elements/TruncatedList.js new file mode 100644 index 0000000000..c5a16e2ff3 --- /dev/null +++ b/src/components/views/elements/TruncatedList.js @@ -0,0 +1,61 @@ +/* +Copyright 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +var React = require('react'); + +module.exports = React.createClass({ + displayName: 'TruncatedList', + + propTypes: { + truncateAt: React.PropTypes.number, + className: React.PropTypes.string, + createOverflowElement: React.PropTypes.func + }, + + getDefaultProps: function() { + return { + truncateAt: 2, + createOverflowElement: function(overflowCount, totalCount) { + return ( +
And {overflowCount} more...
+ ); + } + }; + }, + + render: function() { + var childsJsx = this.props.children; + var overflowJsx; + var childCount = React.Children.count(this.props.children); + var overflowCount = childCount - this.props.truncateAt; + + if (overflowCount > 0) { + overflowJsx = this.props.createOverflowElement( + overflowCount, childCount + ); + var childArray = React.Children.toArray(this.props.children); + // cut out the overflow elements + childArray.splice(childCount - overflowCount, overflowCount); + childsJsx = childArray; // use what is left + } + + return ( +
+ {childsJsx} + {overflowJsx} +
+ ); + } +}); diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index a3e0ee4555..c32cfc868d 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -368,13 +368,14 @@ module.exports = React.createClass({ ); } + var TruncatedList = sdk.getComponent("elements.TruncatedList"); return (
{this.inviteTile()} -
+ {this.makeMemberTiles('join', this.state.searchQuery)} -
+ {invitedSection}
From eed83f982ee1133572b30460cc46d31954e196f8 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 21 Jan 2016 11:41:28 +0000 Subject: [PATCH 2/4] Add a suitable overflow tile for the member list --- src/components/views/elements/TruncatedList.js | 4 ++++ src/components/views/rooms/EntityTile.js | 12 ++++++------ src/components/views/rooms/MemberList.js | 17 ++++++++++++++++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/components/views/elements/TruncatedList.js b/src/components/views/elements/TruncatedList.js index c5a16e2ff3..8007b9a428 100644 --- a/src/components/views/elements/TruncatedList.js +++ b/src/components/views/elements/TruncatedList.js @@ -19,8 +19,12 @@ module.exports = React.createClass({ displayName: 'TruncatedList', propTypes: { + // The number of elements to show before truncating truncateAt: React.PropTypes.number, + // The className to apply to the wrapping div className: React.PropTypes.string, + // A function which will be invoked when an overflow element is required. + // This will be inserted after the children. createOverflowElement: React.PropTypes.func }, diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js index ed0e5cbc41..6b320ce3ff 100644 --- a/src/components/views/rooms/EntityTile.js +++ b/src/components/views/rooms/EntityTile.js @@ -39,7 +39,8 @@ module.exports = React.createClass({ presenceActiveAgo: React.PropTypes.number, showInviteButton: React.PropTypes.bool, shouldComponentUpdate: React.PropTypes.func, - onClick: React.PropTypes.func + onClick: React.PropTypes.func, + suppressOnHover: React.PropTypes.bool }, getDefaultProps: function() { @@ -49,6 +50,7 @@ module.exports = React.createClass({ presenceState: "offline", presenceActiveAgo: -1, showInviteButton: false, + suppressOnHover: false }; }, @@ -75,12 +77,10 @@ module.exports = React.createClass({ var presenceClass = PRESENCE_CLASS[this.props.presenceState] || "mx_EntityTile_offline"; var mainClassName = "mx_EntityTile "; mainClassName += presenceClass; - if (this.state.hover) { - mainClassName += " mx_EntityTile_hover"; - } - var nameEl; - if (this.state.hover) { + + if (this.state.hover && !this.props.suppressOnHover) { + mainClassName += " mx_EntityTile_hover"; var PresenceLabel = sdk.getComponent("rooms.PresenceLabel"); nameEl = (
diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index c32cfc868d..57dcfa96d4 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -260,7 +260,21 @@ module.exports = React.createClass({ return to_display; }, + _createOverflowTile: function(overflowCount, totalCount) { + // For now we'll pretend this is any entity. It should probably be a separate tile. + var EntityTile = sdk.getComponent("rooms.EntityTile"); + var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); + return ( + + } key="overflow" name={`and ${overflowCount} more`} presenceState="online" + suppressOnHover={true} onClick={this._showFullMemberList} /> + ); + }, + _showFullMemberList: function() { + console.log("TODO"); + }, memberSort: function(userIdA, userIdB) { var userA = this.memberDict[userIdA].user; @@ -373,7 +387,8 @@ module.exports = React.createClass({
{this.inviteTile()} - + {this.makeMemberTiles('join', this.state.searchQuery)} {invitedSection} From d72ab641d071ecccfcb06abf226b412417ca471c Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 21 Jan 2016 15:57:59 +0000 Subject: [PATCH 3/4] Expand the list when the overflow element is clicked Negative truncateAt values means "do not truncate". --- .../views/elements/TruncatedList.js | 23 +++++++++++-------- src/components/views/rooms/EntityTile.js | 2 +- src/components/views/rooms/MemberList.js | 20 ++++++++-------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/components/views/elements/TruncatedList.js b/src/components/views/elements/TruncatedList.js index 8007b9a428..7cc584a7c7 100644 --- a/src/components/views/elements/TruncatedList.js +++ b/src/components/views/elements/TruncatedList.js @@ -19,7 +19,7 @@ module.exports = React.createClass({ displayName: 'TruncatedList', propTypes: { - // The number of elements to show before truncating + // The number of elements to show before truncating. If negative, no truncation is done. truncateAt: React.PropTypes.number, // The className to apply to the wrapping div className: React.PropTypes.string, @@ -43,16 +43,19 @@ module.exports = React.createClass({ var childsJsx = this.props.children; var overflowJsx; var childCount = React.Children.count(this.props.children); - var overflowCount = childCount - this.props.truncateAt; - if (overflowCount > 0) { - overflowJsx = this.props.createOverflowElement( - overflowCount, childCount - ); - var childArray = React.Children.toArray(this.props.children); - // cut out the overflow elements - childArray.splice(childCount - overflowCount, overflowCount); - childsJsx = childArray; // use what is left + if (this.props.truncateAt >= 0) { + var overflowCount = childCount - this.props.truncateAt; + + if (overflowCount > 0) { + overflowJsx = this.props.createOverflowElement( + overflowCount, childCount + ); + var childArray = React.Children.toArray(this.props.children); + // cut out the overflow elements + childArray.splice(childCount - overflowCount, overflowCount); + childsJsx = childArray; // use what is left + } } return ( diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js index 6b320ce3ff..43b12006a6 100644 --- a/src/components/views/rooms/EntityTile.js +++ b/src/components/views/rooms/EntityTile.js @@ -45,7 +45,7 @@ module.exports = React.createClass({ getDefaultProps: function() { return { - shouldComponentUpdate: function(nextProps, nextState) { return false; }, + shouldComponentUpdate: function(nextProps, nextState) { return true; }, onClick: function() {}, presenceState: "offline", presenceActiveAgo: -1, diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index 57dcfa96d4..2dafe98494 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -42,7 +42,8 @@ module.exports = React.createClass({ var members = this.roomMembers(INITIAL_LOAD_NUM_MEMBERS); return { - members: members + members: members, + truncateAt: 3 }; }, @@ -75,9 +76,6 @@ module.exports = React.createClass({ self._loadUserList(); }, 50); - - setTimeout - // Attach a SINGLE listener for global presence changes then locate the // member tile and re-render it. This is more efficient than every tile // evar attaching their own listener. @@ -264,16 +262,19 @@ module.exports = React.createClass({ // For now we'll pretend this is any entity. It should probably be a separate tile. var EntityTile = sdk.getComponent("rooms.EntityTile"); var BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); + var text = "and " + overflowCount + " more"; return ( - } key="overflow" name={`and ${overflowCount} more`} presenceState="online" - suppressOnHover={true} onClick={this._showFullMemberList} /> + } name={text} presenceState="online" suppressOnHover={true} + onClick={this._showFullMemberList} /> ); }, _showFullMemberList: function() { - console.log("TODO"); + this.setState({ + truncateAt: -1 + }); }, memberSort: function(userIdA, userIdB) { @@ -341,8 +342,7 @@ module.exports = React.createClass({ return; } memberList.push( - + ) }) } @@ -387,7 +387,7 @@ module.exports = React.createClass({
{this.inviteTile()} - {this.makeMemberTiles('join', this.state.searchQuery)} From dfab32394d118b068e30ecf7437e3681123186e1 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 21 Jan 2016 16:03:32 +0000 Subject: [PATCH 4/4] Set truncation to 10 for now. --- src/components/views/rooms/MemberList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index 2dafe98494..f02d5d6839 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -43,7 +43,7 @@ module.exports = React.createClass({ var members = this.roomMembers(INITIAL_LOAD_NUM_MEMBERS); return { members: members, - truncateAt: 3 + truncateAt: 10 }; },