Merge pull request #4217 from matrix-org/t3chguy/gemini

Remove Gemini Scrollbars
This commit is contained in:
Michael Telatynski 2020-03-18 14:03:51 +00:00 committed by GitHub
commit 42ee157645
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 39 additions and 417 deletions

View file

@ -122,7 +122,6 @@ export default createReactClass({
},
render: function() {
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
if (this.props.devices === null) {
const Spinner = sdk.getComponent("elements.Spinner");
return <Spinner />;
@ -168,7 +167,7 @@ export default createReactClass({
title={_t('Room contains unknown sessions')}
contentId='mx_Dialog_content'
>
<GeminiScrollbarWrapper autoshow={false} className="mx_Dialog_content" id='mx_Dialog_content'>
<div className="mx_Dialog_content" id='mx_Dialog_content'>
<h4>
{ _t('"%(RoomName)s" contains sessions that you haven\'t seen before.', {RoomName: this.props.room.name}) }
</h4>
@ -176,7 +175,7 @@ export default createReactClass({
{ _t("Unknown sessions") }:
<UnknownDeviceList devices={this.props.devices} />
</GeminiScrollbarWrapper>
</div>
<DialogButtons primaryButton={sendButtonLabel}
onPrimaryButtonClick={sendButtonOnClick}
onCancel={this._onDismissClicked} />

View file

@ -1,35 +0,0 @@
/*
Copyright 2018 New Vector 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.
*/
import React from 'react';
import GeminiScrollbar from 'react-gemini-scrollbar';
function GeminiScrollbarWrapper(props) {
const {wrappedRef, ...wrappedProps} = props;
// Enable forceGemini so that gemini is always enabled. This is
// to avoid future issues where a feature is implemented without
// doing QA on every OS/browser combination.
//
// By default GeminiScrollbar allows native scrollbars to be used
// on macOS. Use forceGemini to enable Gemini's non-native
// scrollbars on all OSs.
return <GeminiScrollbar ref={wrappedRef} forceGemini={true} {...wrappedProps}>
{ props.children }
</GeminiScrollbar>;
}
export default GeminiScrollbarWrapper;

View file

@ -27,6 +27,7 @@ import { GroupMemberType } from '../../../groups';
import GroupStore from '../../../stores/GroupStore';
import AccessibleButton from '../elements/AccessibleButton';
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
export default createReactClass({
displayName: 'GroupMemberInfo',
@ -182,10 +183,9 @@ export default createReactClass({
this.props.groupMember.displayname || this.props.groupMember.userId
);
const GeminiScrollbarWrapper = sdk.getComponent('elements.GeminiScrollbarWrapper');
return (
<div className="mx_MemberInfo" role="tabpanel">
<GeminiScrollbarWrapper autoshow={true}>
<AutoHideScrollbar>
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />
</AccessibleButton>
@ -199,7 +199,7 @@ export default createReactClass({
</div>
{ adminTools }
</GeminiScrollbarWrapper>
</AutoHideScrollbar>
</div>
);
},

View file

@ -26,6 +26,7 @@ import { showGroupInviteDialog } from '../../../GroupAddressPicker';
import AccessibleButton from '../elements/AccessibleButton';
import TintableSvg from '../elements/TintableSvg';
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
const INITIAL_LOAD_NUM_MEMBERS = 30;
@ -172,7 +173,6 @@ export default createReactClass({
},
render: function() {
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
if (this.state.fetching || this.state.fetchingInvitedMembers) {
const Spinner = sdk.getComponent("elements.Spinner");
return (<div className="mx_MemberList">
@ -225,10 +225,10 @@ export default createReactClass({
return (
<div className="mx_MemberList" role="tabpanel">
{ inviteButton }
<GeminiScrollbarWrapper autoshow={true}>
<AutoHideScrollbar>
{ joined }
{ invited }
</GeminiScrollbarWrapper>
</AutoHideScrollbar>
{ inputBox }
</div>
);

View file

@ -24,6 +24,7 @@ import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import GroupStore from '../../../stores/GroupStore';
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
export default createReactClass({
displayName: 'GroupRoomInfo',
@ -153,7 +154,6 @@ export default createReactClass({
render: function() {
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
const InlineSpinner = sdk.getComponent('elements.InlineSpinner');
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
if (this.state.groupRoomRemoveLoading || !this.state.groupRoom) {
const Spinner = sdk.getComponent("elements.Spinner");
return <div className="mx_MemberInfo">
@ -216,7 +216,7 @@ export default createReactClass({
const groupRoomName = this.state.groupRoom.displayname;
return (
<div className="mx_MemberInfo" role="tabpanel">
<GeminiScrollbarWrapper autoshow={true}>
<AutoHideScrollbar>
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />
</AccessibleButton>
@ -231,7 +231,7 @@ export default createReactClass({
</div>
{ adminTools }
</GeminiScrollbarWrapper>
</AutoHideScrollbar>
</div>
);
},

View file

@ -22,6 +22,7 @@ import PropTypes from 'prop-types';
import { showGroupAddRoomDialog } from '../../../GroupAddressPicker';
import AccessibleButton from '../elements/AccessibleButton';
import TintableSvg from '../elements/TintableSvg';
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
const INITIAL_LOAD_NUM_ROOMS = 30;
@ -150,17 +151,16 @@ export default createReactClass({
placeholder={_t('Filter community rooms')} autoComplete="off" />
);
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
const TruncatedList = sdk.getComponent("elements.TruncatedList");
return (
<div className="mx_GroupRoomList" role="tabpanel">
{ inviteButton }
<GeminiScrollbarWrapper autoshow={true} className="mx_GroupRoomList_joined mx_GroupRoomList_outerWrapper">
<AutoHideScrollbar className="mx_GroupRoomList_joined mx_GroupRoomList_outerWrapper">
<TruncatedList className="mx_GroupRoomList_wrapper" truncateAt={this.state.truncateAt}
createOverflowElement={this._createOverflowTile}>
{ this.makeGroupRoomTiles(this.state.searchQuery) }
</TruncatedList>
</GeminiScrollbarWrapper>
</AutoHideScrollbar>
{ inputBox }
</div>
);

View file

@ -1,186 +0,0 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
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.
*/
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from "../../../index";
import { _t } from '../../../languageHandler';
// A list capable of displaying entities which conform to the SearchableEntity
// interface which is an object containing getJsx(): Jsx and matches(query: string): boolean
const SearchableEntityList = createReactClass({
displayName: 'SearchableEntityList',
propTypes: {
emptyQueryShowsAll: PropTypes.bool,
showInputBox: PropTypes.bool,
onQueryChanged: PropTypes.func, // fn(inputText)
onSubmit: PropTypes.func, // fn(inputText)
entities: PropTypes.array,
truncateAt: PropTypes.number,
},
getDefaultProps: function() {
return {
showInputBox: true,
entities: [],
emptyQueryShowsAll: false,
onSubmit: function() {},
onQueryChanged: function(input) {},
};
},
getInitialState: function() {
return {
query: "",
focused: false,
truncateAt: this.props.truncateAt,
results: this.getSearchResults("", this.props.entities),
};
},
componentWillReceiveProps: function(newProps) {
// recalculate the search results in case we got new entities
this.setState({
results: this.getSearchResults(this.state.query, newProps.entities),
});
},
componentWillUnmount: function() {
// pretend the query box was blanked out else filters could still be
// applied to other components which rely on onQueryChanged.
this.props.onQueryChanged("");
},
/**
* Public-facing method to set the input query text to the given input.
* @param {string} input
*/
setQuery: function(input) {
this.setState({
query: input,
results: this.getSearchResults(input, this.props.entities),
});
},
onQueryChanged: function(ev) {
const q = ev.target.value;
this.setState({
query: q,
// reset truncation if they back out the entire text
truncateAt: (q.length === 0 ? this.props.truncateAt : this.state.truncateAt),
results: this.getSearchResults(q, this.props.entities),
}, () => {
// invoke the callback AFTER we've flushed the new state. We need to
// do this because onQueryChanged can result in new props being passed
// to this component, which will then try to recalculate the search
// list. If we do this without flushing, we'll recalc with the last
// search term and not the current one!
this.props.onQueryChanged(q);
});
},
onQuerySubmit: function(ev) {
ev.preventDefault();
this.props.onSubmit(this.state.query);
},
getSearchResults: function(query, entities) {
if (!query || query.length === 0) {
return this.props.emptyQueryShowsAll ? entities : [];
}
return entities.filter(function(e) {
return e.matches(query);
});
},
_showAll: function() {
this.setState({
truncateAt: -1,
});
},
_createOverflowEntity: function(overflowCount, totalCount) {
const EntityTile = sdk.getComponent("rooms.EntityTile");
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
const text = _t("and %(count)s others...", { count: overflowCount });
return (
<EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
<BaseAvatar url={require("../../../../res/img/ellipsis.svg")} name="..." width={36} height={36} />
} name={text} presenceState="online" suppressOnHover={true}
onClick={this._showAll} />
);
},
render: function() {
let inputBox;
if (this.props.showInputBox) {
inputBox = (
<form onSubmit={this.onQuerySubmit} autoComplete="off">
<input className="mx_SearchableEntityList_query" id="mx_SearchableEntityList_query" type="text"
onChange={this.onQueryChanged} value={this.state.query}
onFocus= {() => { this.setState({ focused: true }); }}
onBlur= {() => { this.setState({ focused: false }); }}
placeholder={_t("Search")} />
</form>
);
}
let list;
if (this.state.results.length > 1 || this.state.focused) {
if (this.props.truncateAt) { // caller wants list truncated
const TruncatedList = sdk.getComponent("elements.TruncatedList");
list = (
<TruncatedList className="mx_SearchableEntityList_list"
truncateAt={this.state.truncateAt} // use state truncation as it may be expanded
createOverflowElement={this._createOverflowEntity}>
{ this.state.results.map((entity) => {
return entity.getJsx();
}) }
</TruncatedList>
);
} else {
list = (
<div className="mx_SearchableEntityList_list">
{ this.state.results.map((entity) => {
return entity.getJsx();
}) }
</div>
);
}
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
list = (
<GeminiScrollbarWrapper autoshow={true}
className="mx_SearchableEntityList_listWrapper">
{ list }
</GeminiScrollbarWrapper>
);
}
return (
<div className={"mx_SearchableEntityList " + (list ? "mx_SearchableEntityList_expanded" : "")}>
{ inputBox }
{ list }
{ list ? <div className="mx_SearchableEntityList_hrWrapper"><hr /></div> : '' }
</div>
);
},
});
export default SearchableEntityList;