Merge pull request #2267 from matrix-org/bwindels/roomfilterfield

Redesign: bring back & restyle room filter field
This commit is contained in:
Bruno Windels 2018-11-05 13:28:23 +00:00 committed by GitHub
commit f8f4dde3ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 311 additions and 280 deletions

View file

@ -52,11 +52,6 @@ a:visited {
input[type=text], input[type=password], textarea {
background-color: transparent;
color: $primary-fg-color;
}
input[type=text].error, input[type=password].error {
border: 1px solid $warning-color;
}
input[type=text]:focus, input[type=password]:focus, textarea:focus {

View file

@ -14,55 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_SearchBox {
height: 24px;
margin-left: 16px;
margin-right: 16px;
padding-top: 24px;
padding-bottom: 22px;
border-bottom: 1px solid $panel-divider-color;
display: flex;
}
.mx_SearchBox_searchButton {
margin-right: 10px;
margin-top: 5px;
pointer-events: none;
}
.mx_SearchBox_closeButton {
cursor: pointer;
margin-top: -5px;
}
.mx_SearchBox_search {
flex: 1 1 auto;
width: 0px;
font-family: $font-family;
font-size: 12px;
margin-top: -2px;
height: 24px;
border: 0px ! important;
/* border-bottom: 1px solid rgba(0, 0, 0, 0.1) ! important; */
border: 0px;
}
.mx_SearchBox_minimise,
.mx_SearchBox_maximise {
margin-top: 3px;
cursor: pointer;
}
.mx_SearchBox_minimise {
margin-left: 10px;
}
.mx_SearchBox_maximise {
margin-left: 9px;
}
.mx_SearchBox object {
pointer-events: none;
background-image: url('../../img/icons-close.svg');
background-repeat: no-repeat;
width: 16px;
height: 16px;
background-position: center;
padding: 9px;
}

View file

@ -43,41 +43,21 @@ limitations under the License.
.mx_MemberList form,
.mx_GroupMemberList form,
.mx_GroupRoomList form {
margin: 8px;
display: flex;
justify-content: flex-end;
align-items: center;
}
.mx_MemberList form > *:not(:first-child),
.mx_GroupMemberList form > *:not(:first-child),
.mx_GroupRoomList form > *:not(:first-child) {
margin-left: 5px;
.mx_MemberList form > *,
.mx_GroupMemberList form > *,
.mx_GroupRoomList form > * {
margin: 9px;
}
.mx_MemberList_query,
.mx_GroupMemberList_query,
.mx_GroupRoomList_query {
flex: 1 1 0;
box-sizing: border-box;
font-family: $font-family;
border-radius: 4px;
padding: 9px;
color: $input-darker-fg-color;
background-color: $input-darker-bg-color;
font-size: 14px;
font-weight: 600;
border: none;
}
.mx_MemberList_query::-webkit-input-placeholder,
.mx_GroupMemberList_query::-webkit-input-placeholder,
.mx_GroupRoomList_query::-webkit-input-placeholder,
.mx_MemberList_query::-moz-placeholder,
.mx_GroupMemberList_query::-moz-placeholder,
.mx_GroupRoomList_query::-moz-placeholder {
color: $input-darker-fg-color;
opacity: 0.5;
font-size: 14px;
}
.mx_MemberList h2, .mx_GroupMemberList h2 {

View file

@ -1,23 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-11 13 24 24" xml:space="preserve">
<style type="text/css">
.st1{fill:none;stroke-width:2;stroke-linecap:round;}
</style>
<title>icons_create_room</title>
<desc>Created with sketchtool.</desc>
<g id="_x30_3-Input" sketch:type="MSPage">
<g id="_x30_3_x5F_4-Uploading" transform="translate(-20.000000, -726.000000)" sketch:type="MSArtboardGroup">
<g id="Room-list" sketch:type="MSLayerGroup">
<g id="Room-list_x2F_Footer" transform="translate(0.000000, 708.000000)" sketch:type="MSShapeGroup">
<g id="icons_create_room" transform="translate(20.000000, 18.000000)">
<circle id="Oval-1-Copy-7" fill="#76CFA6" cx="1" cy="25" r="12"/>
<path id="Line" class="st1" stroke="#FFFFFF" d="M-2.5,28.5l7.1-7.1"/>
<path id="Line_1_" class="st1" stroke="#FFFFFF" d="M-2.5,21.5l7.1,7.1"/>
</g>
</g>
</g>
</g>
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="-11 13 9.1202002 9.2202005"
xml:space="preserve"
sodipodi:docname="icons-close.svg"
width="9.1202002"
height="9.2202005"
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
id="metadata21"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title>icons_create_room</dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs19" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1016"
id="namedview17"
showgrid="false"
showguides="false"
inkscape:zoom="9.8333333"
inkscape:cx="-0.15658729"
inkscape:cy="-1.4622263"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="icons_create_room"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<style
type="text/css"
id="style2">
.st1{fill:none;stroke-width:2;stroke-linecap:round;}
</style>
<title
id="title4">icons_create_room</title>
<desc
id="desc6">Created with sketchtool.</desc>
<g
id="_x30_3-Input"
sketch:type="MSPage"
transform="translate(-7.4898998,-7.3898998)">
<g
id="_x30_3_x5F_4-Uploading"
transform="translate(-20,-726)"
sketch:type="MSArtboardGroup">
<g
id="Room-list"
sketch:type="MSLayerGroup">
<g
id="Room-list_x2F_Footer"
transform="translate(0,708)"
sketch:type="MSShapeGroup">
<g
id="icons_create_room"
transform="translate(20,18)">
<path
id="Line"
class="st1"
d="M -2.5,28.5 4.6,21.4"
style="fill:none;stroke:#9fa9ba;stroke-width:2;stroke-linecap:round;stroke-opacity:1"
inkscape:connector-curvature="0" />
<path
id="Line_1_"
class="st1"
d="m -2.5,21.5 7.1,7.1"
style="fill:none;stroke:#9fa9ba;stroke-width:2;stroke-linecap:round;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="13" height="13" viewBox="0, 0, 13, 13">
<g id="Symbols">
<g>
<path d="M10.229,5.547 C10.229,8.133 8.133,10.229 5.547,10.229 C2.961,10.229 0.865,8.133 0.865,5.547 C0.865,2.961 2.961,0.865 5.547,0.865 C8.133,0.865 10.229,2.961 10.229,5.547 z" fill-opacity="0" stroke="#76CFA6" stroke-width="1" stroke-linecap="round" id="path-1" opacity="0.7"/>
<path d="M8.824,8.824 L12.135,12.135" fill-opacity="0" stroke="#76CFA6" stroke-width="1" stroke-linecap="round" id="Line" opacity="0.7"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 776 B

View file

@ -1,8 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="35" height="35" viewBox="0, 0, 35, 35">
<g id="Symbols">
<path d="M22.4,15.4 C22.4,19.266 19.266,22.4 15.4,22.4 C11.534,22.4 8.4,19.266 8.4,15.4 C8.4,11.534 11.534,8.4 15.4,8.4 C19.266,8.4 22.4,11.534 22.4,15.4 z" stroke="#9FA9BA" stroke-width="1" stroke-linecap="round" id="path-1" fill="none"/>
<path d="M20.3,20.3 L25.25,25.25" stroke="#9FA9BA" stroke-width="1" stroke-linecap="round" id="Line"/>
</g>
</svg>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.3 (67297) - http://www.bohemiancoding.com/sketch -->
<title>Shape</title>
<desc>Created with Sketch.</desc>
<g id="agreed" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="matrix-files-list" transform="translate(-96.000000, -71.000000)" fill="#9FA9BA" fill-rule="nonzero">
<g id="1-copy" transform="translate(67.000000, 47.000000)">
<g id="search" transform="translate(17.000000, 16.000000)">
<path d="M27.7960817,22.7689061 L24.2910448,19.2638692 C26.6040192,16.4045943 26.2742638,12.2351072 23.5405883,9.77496046 C20.8069128,7.31481375 16.6258535,7.42483371 14.0253436,10.0253436 C11.4248337,12.6258535 11.3148138,16.8069128 13.7749605,19.5405883 C16.2351072,22.2742638 20.4045943,22.6040192 23.2638692,20.2910448 L26.7689061,23.7960817 C27.0539429,24.0713794 27.5070231,24.0674423 27.7872327,23.7872327 C28.0674423,23.5070231 28.0713794,23.0539429 27.7960817,22.7689061 Z M13.4802761,14.9285199 C13.4778286,12.4654797 15.1281652,10.3071276 17.5057384,9.66391787 C19.8833116,9.02070817 22.3967204,10.0526338 23.6363865,12.1809668 C24.8760525,14.3092997 24.5336194,17.0046354 22.8011317,18.7553664 C22.7924145,18.7633572 22.781518,18.7655365 22.7735272,18.7735272 C22.7655365,18.781518 22.7626308,18.7931409 22.7553664,18.8011317 C21.1935085,20.3473331 18.8551963,20.8029027 16.8271177,19.9561226 C14.799039,19.1093426 13.4789257,17.1262773 13.4802761,14.9285199 Z" id="Shape"/>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 678 B

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="21px" height="19px" viewBox="0 0 21 19" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch -->
<title>icons_search</title>
<desc>Created with bin/sketchtool.</desc>
<defs></defs>
<g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="02_13-Chat-member-profile" sketch:type="MSArtboardGroup" transform="translate(-910.000000, -34.000000)" stroke="#76CFA6">
<g id="icons_search" sketch:type="MSLayerGroup" transform="translate(906.000000, 30.000000)">
<path d="M17.328421,15.3333333 L23.5542961,20.7357275 C23.9574623,21.085568 24.0116667,21.70516 23.6700827,22.1261351 L23.6700827,22.1261351 C23.3308636,22.5441955 22.72562,22.5965299 22.3258751,22.2496583 L16.1,16.8472641" id="Rectangle-9" sketch:type="MSShapeGroup"></path>
<g id="search" transform="translate(11.617851, 11.853553) rotate(-45.000000) translate(-11.617851, -11.853553) translate(4.117851, 3.853553)" sketch:type="MSShapeGroup">
<ellipse id="Search" cx="7.64433504" cy="7.90518519" rx="7.1665641" ry="7.41111111"></ellipse>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -69,6 +69,8 @@ $primary-hairline-color: #e5e5e5;
$input-border-color: #f0f0f0;
$input-darker-bg-color: rgba(193, 201, 214, 0.29);
$input-darker-fg-color: #9fa9ba;
$input-lighter-bg-color: #f2f5f8;
$input-lighter-fg-color: $input-darker-fg-color;
$button-bg-color: #7ac9a1;
$button-fg-color: white;
@ -178,6 +180,93 @@ $lightbox-border-color: #ffffff;
// unused?
$progressbar-color: #000;
// form elements
// .mx_textinput is a container for a text input
// + some other controls like buttons, ...
// it has the appearance of a text box so the controls
// appear to be part of the input
:not(.mx_textinput) > input[type=text],
:not(.mx_textinput) > input[type=search],
.mx_textinput {
display: block;
margin: 9px;
box-sizing: border-box;
background-color: transparent;
color: $input-darker-fg-color;
border-radius: 4px;
border: 1px solid #c1c1c1;
}
.mx_textinput {
display: flex;
align-items: center;
}
.mx_textinput > input[type=text],
.mx_textinput > input[type=search] {
border: none;
flex: 1;
color: inherit; //from .mx_textinput
}
input[type=text],
input[type=search] {
padding: 9px;
font-family: $font-family;
font-size: 14px;
font-weight: 600;
min-width: 0;
}
.dark-panel {
:not(.mx_textinput) > input[type=text],
:not(.mx_textinput) > input[type=search],
.mx_textinput {
color: $input-darker-fg-color;
background-color: $input-darker-bg-color;
border: none;
}
}
.light-panel {
:not(.mx_textinput) > input[type=text],
:not(.mx_textinput) > input[type=search],
.mx_textinput {
color: $input-lighter-fg-color;
background-color: $input-lighter-bg-color;
border: none;
}
}
input[type=text].mx_textinput_icon,
input[type=search].mx_textinput_icon {
padding-left: 36px;
background-repeat: no-repeat;
background-position: 10px center;
}
input[type=text].mx_textinput_icon.mx_textinput_search,
input[type=search].mx_textinput_icon.mx_textinput_search {
background-image: url('../../img/icons-search.svg');
}
// dont search UI as not all browsers support it,
// we implement it ourselves where needed instead
input[type=search]::-webkit-search-decoration,
input[type=search]::-webkit-search-cancel-button,
input[type=search]::-webkit-search-results-button,
input[type=search]::-webkit-search-results-decoration {
display: none;
}
.input[type=text]::-webkit-input-placeholder,
.input[type=text]::-moz-placeholder,
.input[type=search]::-webkit-input-placeholder,
.input[type=search]::-moz-placeholder {
color: #a5aab2;
}
// ***** Mixins! *****
@define-mixin mx_DialogButton {

View file

@ -1286,9 +1286,10 @@ export default React.createClass({
const rightPanel = !this.props.collapsedRhs ? <RightPanel groupId={this.props.groupId} /> : undefined;
const headerClasses = {
mx_GroupView_header: true,
mx_GroupView_header_view: !this.state.editing,
mx_GroupView_header_isUserMember: this.state.isUserMember,
"mx_GroupView_header": true,
"light-panel": true,
"mx_GroupView_header_view": !this.state.editing,
"mx_GroupView_header_isUserMember": this.state.isUserMember,
};
return (

View file

@ -171,6 +171,12 @@ const LeftPanel = React.createClass({
this.setState({ searchFilter: term });
},
onSearchCleared: function(source) {
if (source === "keyboard") {
dis.dispatch({action: 'focus_composer'});
}
},
collectRoomList: function(ref) {
this._roomList = ref;
},
@ -179,13 +185,9 @@ const LeftPanel = React.createClass({
const RoomList = sdk.getComponent('rooms.RoomList');
const TagPanel = sdk.getComponent('structures.TagPanel');
const TopLeftMenuButton = sdk.getComponent('structures.TopLeftMenuButton');
const SearchBox = sdk.getComponent('structures.SearchBox');
const CallPreview = sdk.getComponent('voip.CallPreview');
const topBox = <TopLeftMenuButton collapsed={ this.props.collapsed } />;
/*
const SearchBox = sdk.getComponent('structures.SearchBox');
const topBox = <SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } />;
*/
const tagPanelEnabled = !SettingsStore.getValue("TagPanel.disableTagPanel");
const tagPanel = tagPanelEnabled ? <TagPanel /> : <div />;
@ -198,11 +200,16 @@ const LeftPanel = React.createClass({
},
);
const searchBox = !this.props.collapsed ?
<SearchBox onSearch={ this.onSearch } onCleared={ this.onSearchCleared } /> :
undefined;
return (
<div className={containerClasses}>
{ tagPanel }
<aside className={"mx_LeftPanel"} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
{ topBox }
<aside className={"mx_LeftPanel dark-panel"} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
<TopLeftMenuButton collapsed={ this.props.collapsed } />
{ searchBox }
<CallPreview ConferenceHandler={VectorConferenceHandler} />
<RoomList
ref={this.collectRoomList}

View file

@ -52,7 +52,7 @@ const RoomSubList = React.createClass({
collapsed: PropTypes.bool.isRequired, // is LeftPanel collapsed?
onHeaderClick: PropTypes.func,
incomingCall: PropTypes.object,
searchFilter: PropTypes.string,
isFiltered: PropTypes.bool,
headerItems: PropTypes.node, // content shown in the sublist header
extraTiles: PropTypes.arrayOf(PropTypes.node), // extra elements added beneath tiles
},
@ -60,7 +60,6 @@ const RoomSubList = React.createClass({
getInitialState: function() {
return {
hidden: this.props.startAsHidden || false,
sortedList: [],
};
},
@ -74,9 +73,6 @@ const RoomSubList = React.createClass({
},
componentWillMount: function() {
this.setState({
sortedList: this.applySearchFilter(this.props.list, this.props.searchFilter),
});
this.dispatcherRef = dis.register(this.onAction);
},
@ -84,23 +80,6 @@ const RoomSubList = React.createClass({
dis.unregister(this.dispatcherRef);
},
componentWillReceiveProps: function(newProps) {
// order the room list appropriately before we re-render
//if (debug) console.log("received new props, list = " + newProps.list);
this.setState({
sortedList: this.applySearchFilter(newProps.list, newProps.searchFilter),
});
},
applySearchFilter: function(list, filter) {
if (filter === "") return list;
const lcFilter = filter.toLowerCase();
// case insensitive if room name includes filter,
// or if starts with `#` and one of room's aliases starts with filter
return list.filter((room) => (room.name && room.name.toLowerCase().includes(lcFilter)) ||
(filter[0] === '#' && room.getAliases().some((alias) => alias.toLowerCase().startsWith(lcFilter))));
},
// The header is collapsable if it is hidden or not stuck
// The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
isCollapsableOnClick: function() {
@ -196,7 +175,7 @@ const RoomSubList = React.createClass({
makeRoomTiles: function() {
const RoomTile = sdk.getComponent("rooms.RoomTile");
return this.state.sortedList.map((room, index) => {
return this.props.list.map((room, index) => {
return <RoomTile
room={room}
roomSubList={this}
@ -218,7 +197,7 @@ const RoomSubList = React.createClass({
e.preventDefault();
e.stopPropagation();
// find first room which has notifications and switch to it
for (const room of this.state.sortedList) {
for (const room of this.props.list) {
const roomNotifState = RoomNotifs.getRoomNotifsState(room.roomId);
const highlight = room.getUnreadNotificationCount('highlight') > 0;
const notificationCount = room.getUnreadNotificationCount();
@ -241,10 +220,10 @@ const RoomSubList = React.createClass({
e.preventDefault();
e.stopPropagation();
// switch to first room in sortedList as that'll be the top of the list for the user
if (this.state.sortedList && this.state.sortedList.length > 0) {
if (this.props.list && this.props.list.length > 0) {
dis.dispatch({
action: 'view_room',
room_id: this.state.sortedList[0].roomId,
room_id: this.props.list[0].roomId,
});
} else if (this.props.extraTiles && this.props.extraTiles.length > 0) {
// Group Invites are different in that they are all extra tiles and not rooms
@ -312,7 +291,7 @@ const RoomSubList = React.createClass({
);
}
const len = this.state.sortedList.length + this.props.extraTiles.length;
const len = this.props.list.length + this.props.extraTiles.length;
let chevron;
if (len) {
const chevronClasses = classNames({
@ -323,7 +302,7 @@ const RoomSubList = React.createClass({
chevron = (<div className={chevronClasses}></div>);
}
const tabindex = this.props.searchFilter === "" ? "0" : "-1";
const tabindex = this.props.isFiltered ? "0" : "-1";
return (
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
<AccessibleButton onClick={ this.onClick } className="mx_RoomSubList_label" tabIndex={tabindex}>
@ -338,7 +317,7 @@ const RoomSubList = React.createClass({
},
render: function() {
const len = this.state.sortedList.length + this.props.extraTiles.length;
const len = this.props.list.length + this.props.extraTiles.length;
if (len) {
const subListClasses = classNames({
"mx_RoomSubList": true,

View file

@ -28,8 +28,8 @@ module.exports = React.createClass({
displayName: 'SearchBox',
propTypes: {
collapsed: React.PropTypes.bool,
onSearch: React.PropTypes.func,
onCleared: React.PropTypes.func,
},
getInitialState: function() {
@ -75,86 +75,44 @@ module.exports = React.createClass({
100,
),
onToggleCollapse: function(show) {
if (show) {
dis.dispatch({
action: 'show_left_panel',
});
} else {
dis.dispatch({
action: 'hide_left_panel',
});
}
},
_onKeyDown: function(ev) {
switch (ev.keyCode) {
case KeyCode.ESCAPE:
this._clearSearch();
dis.dispatch({action: 'focus_composer'});
this._clearSearch("keyboard");
break;
}
},
_clearSearch: function() {
_clearSearch: function(source) {
this.refs.search.value = "";
this.onChange();
if (this.props.onCleared) {
this.props.onCleared(source);
}
},
render: function() {
const TintableSvg = sdk.getComponent('elements.TintableSvg');
const collapseTabIndex = this.refs.search && this.refs.search.value !== "" ? "-1" : "0";
const clearButton = this.state.searchTerm.length > 0 ?
(<AccessibleButton key="button"
className="mx_SearchBox_closeButton"
onClick={ () => {this._clearSearch("button")} }>
</AccessibleButton>) : undefined;
let toggleCollapse;
if (this.props.collapsed) {
toggleCollapse =
<AccessibleButton className="mx_SearchBox_maximise" tabIndex={collapseTabIndex} onClick={ this.onToggleCollapse.bind(this, true) }>
<TintableSvg src="img/maximise.svg" width="10" height="16" alt={ _t("Expand panel") } />
</AccessibleButton>;
} else {
toggleCollapse =
<AccessibleButton className="mx_SearchBox_minimise" tabIndex={collapseTabIndex} onClick={ this.onToggleCollapse.bind(this, false) }>
<TintableSvg src="img/minimise.svg" width="10" height="16" alt={ _t("Collapse panel") } />
</AccessibleButton>;
}
let searchControls;
if (!this.props.collapsed) {
searchControls = [
this.state.searchTerm.length > 0 ?
<AccessibleButton key="button"
className="mx_SearchBox_closeButton"
onClick={ ()=>{ this._clearSearch(); } }>
<TintableSvg
className="mx_SearchBox_searchButton"
src="img/icons-close.svg" width="24" height="24"
/>
</AccessibleButton>
:
<TintableSvg
key="button"
className="mx_SearchBox_searchButton"
src="img/icons-search-copy.svg" width="13" height="13"
/>,
<input
key="searchfield"
type="text"
ref="search"
className="mx_SearchBox_search"
value={ this.state.searchTerm }
onChange={ this.onChange }
onKeyDown={ this._onKeyDown }
placeholder={ _t('Filter room names') }
/>,
];
}
const self = this;
return (
<div className="mx_SearchBox">
{ searchControls }
{ toggleCollapse }
<div className="mx_SearchBox mx_textinput">
<input
key="searchfield"
type="text"
ref="search"
className="mx_textinput_icon mx_textinput_search"
value={ this.state.searchTerm }
onChange={ this.onChange }
onKeyDown={ this._onKeyDown }
placeholder={ _t('Filter room names') }
/>
{ clearButton }
</div>
);
},

View file

@ -163,7 +163,7 @@ export default React.createClass({
</div> : <div />;
return (
<div className="mx_MemberList">
<div className="mx_MemberList dark-panel">
{ inputBox }
<GeminiScrollbarWrapper autoshow={true}>
{ joined }

View file

@ -131,7 +131,7 @@ export default React.createClass({
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
const TruncatedList = sdk.getComponent("elements.TruncatedList");
return (
<div className="mx_GroupRoomList">
<div className="mx_GroupRoomList dark-panel">
{ inputBox }
<GeminiScrollbarWrapper autoshow={true} className="mx_GroupRoomList_joined mx_GroupRoomList_outerWrapper">
<TruncatedList className="mx_GroupRoomList_wrapper" truncateAt={this.state.truncateAt}

View file

@ -449,7 +449,7 @@ module.exports = React.createClass({
const filterAndButtons = (
<form autoComplete="off">
<input className="mx_MemberList_query" id="mx_MemberList_query" type="search"
<input className="mx_MemberList_query mx_textinput_icon mx_textinput_search" id="mx_MemberList_query" type="search"
onChange={this.onSearchQueryChanged} value={this.state.searchQuery}
placeholder={_t('Filter room members')} />
{ inviteButton }
@ -457,7 +457,7 @@ module.exports = React.createClass({
);
return (
<div className="mx_MemberList">
<div className="mx_MemberList dark-panel">
{ filterAndButtons }
<GeminiScrollbarWrapper autoshow={true}>
<div className="mx_MemberList_wrapper">

View file

@ -382,7 +382,7 @@ module.exports = React.createClass({
if (this.props.onSearchClick && this.props.inRoom) {
searchButton =
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onSearchClick} title={_t("Search")}>
<TintableSvg src="img/icons-search.svg" width="35" height="35" />
<TintableSvg src="img/icons-search.svg" width="16" height="16" />
</AccessibleButton>;
}
@ -424,7 +424,7 @@ module.exports = React.createClass({
}
return (
<div className={"mx_RoomHeader " + (this.props.editing ? "mx_RoomHeader_editing" : "")}>
<div className={"mx_RoomHeader light-panel " + (this.props.editing ? "mx_RoomHeader_editing" : "")}>
<div className="mx_RoomHeader_wrapper">
<div className="mx_RoomHeader_avatar">{ roomAvatar }</div>
{ name }

View file

@ -33,8 +33,9 @@ const Receipt = require('../../../utils/Receipt');
import TagOrderStore from '../../../stores/TagOrderStore';
import RoomListStore from '../../../stores/RoomListStore';
import GroupStore from '../../../stores/GroupStore';
import RoomSubList from '../../structures/RoomSubList';
import ResizeHandle from '../elements/ResizeHandle';
import {Resizer, FixedDistributor, FlexSizer} from '../../../resizer'
const HIDE_CONFERENCE_CHANS = true;
const STANDARD_TAGS_REGEX = /^(m\.(favourite|lowpriority|server_notice)|im\.vector\.fake\.(invite|recent|direct|archived))$/;
@ -440,39 +441,50 @@ module.exports = React.createClass({
return ret;
},
_applySearchFilter: function(list, filter) {
if (filter === "") return list;
const lcFilter = filter.toLowerCase();
// case insensitive if room name includes filter,
// or if starts with `#` and one of room's aliases starts with filter
return list.filter((room) => (room.name && room.name.toLowerCase().includes(lcFilter)) ||
(filter[0] === '#' && room.getAliases().some((alias) => alias.toLowerCase().startsWith(lcFilter))));
},
_mapSubListProps: function(subListsProps) {
const defaultProps = {
collapsed: this.props.collapsed,
isFiltered: !!this.props.searchFilter,
incomingCall: this.state.incomingCall,
};
subListsProps.forEach((p) => {
p.list = this._applySearchFilter(p.list, this.props.searchFilter);
});
subListsProps = subListsProps.filter((props => {
const len = props.list.length + (props.extraTiles ? props.extraTiles.length : 0);
return len !== 0 || (props.onAddRoom && !this.props.searchFilter);
}));
return subListsProps.reduce((components, props, i) => {
props = Object.assign({}, defaultProps, props);
const isLast = i === subListsProps.length - 1;
const {key, label, ... otherProps} = props;
const chosenKey = key || label;
let subList = <RoomSubList key={chosenKey} label={label} {...otherProps} />;
if (!isLast) {
return components.concat(
subList,
<ResizeHandle key={chosenKey+"-resizer"} vertical={true} />
);
} else {
return components.concat(subList);
}
}, []);
},
render: function() {
const RoomSubList = sdk.getComponent('structures.RoomSubList');
const mapProps = (subListsProps) => {
const defaultProps = {
collapsed: this.props.collapsed,
searchFilter: this.props.searchFilter,
incomingCall: this.state.incomingCall,
};
subListsProps = subListsProps.filter((props => {
const len = props.list.length + (props.extraTiles ? props.extraTiles.length : 0);
return len !== 0 || props.onAddRoom;
}));
return subListsProps.reduce((components, props, i) => {
props = Object.assign({}, defaultProps, props);
const isLast = i === subListsProps.length - 1;
const {key, label, ... otherProps} = props;
const chosenKey = key || label;
let subList = <RoomSubList key={chosenKey} label={label} {...otherProps} />;
if (!isLast) {
return components.concat(
subList,
<ResizeHandle key={chosenKey+"-resizer"} vertical={true} />
);
} else {
return components.concat(subList);
}
}, []);
}
let subLists = [
{
list: [],
@ -545,7 +557,7 @@ module.exports = React.createClass({
},
]);
const subListComponents = mapProps(subLists);
const subListComponents = this._mapSubListProps(subLists);
return (
<div ref={(d) => this.resizeContainer = d} className="mx_RoomList">