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

@ -72,7 +72,6 @@
"flux": "2.1.1", "flux": "2.1.1",
"focus-visible": "^5.0.2", "focus-visible": "^5.0.2",
"fuse.js": "^2.2.0", "fuse.js": "^2.2.0",
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#91e1e566",
"gfm.css": "^1.1.1", "gfm.css": "^1.1.1",
"glob-to-regexp": "^0.4.1", "glob-to-regexp": "^0.4.1",
"highlight.js": "^9.15.8", "highlight.js": "^9.15.8",
@ -93,7 +92,6 @@
"react-beautiful-dnd": "^4.0.1", "react-beautiful-dnd": "^4.0.1",
"react-dom": "^16.9.0", "react-dom": "^16.9.0",
"react-focus-lock": "^2.2.1", "react-focus-lock": "^2.2.1",
"react-gemini-scrollbar": "github:matrix-org/react-gemini-scrollbar#9cf17f63b7c0b0ec5f31df27da0f82f7238dc594",
"resize-observer-polyfill": "^1.5.0", "resize-observer-polyfill": "^1.5.0",
"sanitize-html": "^1.18.4", "sanitize-html": "^1.18.4",
"text-encoding-utf-8": "^1.0.1", "text-encoding-utf-8": "^1.0.1",

View file

@ -207,37 +207,6 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
transition: opacity 0.2s ease-in-out; transition: opacity 0.2s ease-in-out;
} }
/* XXX: critical hack to GeminiScrollbar to allow them to work in FF 42 and Chrome 48.
Stop the scrollbar view from pushing out the container's overall sizing, which causes
flexbox to adapt to the new size and cause the view to keep growing.
*/
.gm-scrollbar-container .gm-scroll-view {
position: absolute;
}
/* Expand thumbs on hoverover */
.gm-scrollbar {
border-radius: 5px !important;
}
.gm-scrollbar.-vertical {
width: 6px;
transition: width 120ms ease-out !important;
}
.gm-scrollbar.-vertical:hover,
.gm-scrollbar.-vertical:active {
width: 8px;
transition: width 120ms ease-out !important;
}
.gm-scrollbar.-horizontal {
height: 6px;
transition: height 120ms ease-out !important;
}
.gm-scrollbar.-horizontal:hover,
.gm-scrollbar.-horizontal:active {
height: 8px;
transition: height 120ms ease-out !important;
}
// These are magic constants which are excluded from tinting, to let themes // These are magic constants which are excluded from tinting, to let themes
// (which only have CSS, unlike skins) tell the app what their non-tinted // (which only have CSS, unlike skins) tell the app what their non-tinted
// colourscheme is by inspecting the stylesheet DOM. // colourscheme is by inspecting the stylesheet DOM.

View file

@ -177,7 +177,6 @@
@import "./views/rooms/_RoomTile.scss"; @import "./views/rooms/_RoomTile.scss";
@import "./views/rooms/_RoomUpgradeWarningBar.scss"; @import "./views/rooms/_RoomUpgradeWarningBar.scss";
@import "./views/rooms/_SearchBar.scss"; @import "./views/rooms/_SearchBar.scss";
@import "./views/rooms/_SearchableEntityList.scss";
@import "./views/rooms/_SendMessageComposer.scss"; @import "./views/rooms/_SendMessageComposer.scss";
@import "./views/rooms/_Stickers.scss"; @import "./views/rooms/_Stickers.scss";
@import "./views/rooms/_TopUnreadMessagesBar.scss"; @import "./views/rooms/_TopUnreadMessagesBar.scss";

View file

@ -180,10 +180,6 @@ limitations under the License.
line-height: 2em; line-height: 2em;
} }
.mx_GroupView > .mx_MainSplit {
flex: 1;
}
.mx_GroupView_body { .mx_GroupView_body {
flex-grow: 1; flex-grow: 1;
} }
@ -341,8 +337,8 @@ limitations under the License.
display: none; display: none;
} }
.mx_GroupView_body .gm-scroll-view > * { .mx_GroupView_body .mx_AutoHideScrollbar_offset > * {
margin: 11px 50px 0px 68px; margin: 11px 50px 50px 68px;
} }
.mx_GroupView_groupDesc textarea { .mx_GroupView_groupDesc textarea {
@ -370,7 +366,7 @@ limitations under the License.
padding: 40px 20px; padding: 40px 20px;
} }
.mx_GroupView .mx_MemberInfo .gm-scroll-view > :not(.mx_MemberInfo_avatar) { .mx_GroupView .mx_MemberInfo .mx_AutoHideScrollbar_offset > :not(.mx_MemberInfo_avatar) {
padding-left: 16px; padding-left: 16px;
padding-right: 16px; padding-right: 16px;
} }

View file

@ -18,6 +18,7 @@ limitations under the License.
display: flex; display: flex;
flex-direction: row; flex-direction: row;
min-width: 0; min-width: 0;
height: 100%;
} }
// move hit area 5px to the right so it doesn't overlap with the timeline scrollbar // move hit area 5px to the right so it doesn't overlap with the timeline scrollbar

View file

@ -76,13 +76,6 @@ limitations under the License.
flex: 1 1 0; flex: 1 1 0;
min-width: 0; min-width: 0;
/* Experimental fix for https://github.com/vector-im/vector-web/issues/947
and https://github.com/vector-im/vector-web/issues/946.
Empirically this stops the MessagePanel's width exploding outwards when
gemini is in 'prevented' mode
*/
overflow-x: auto;
/* To fix https://github.com/vector-im/riot-web/issues/3298 where Safari /* To fix https://github.com/vector-im/riot-web/issues/3298 where Safari
needed height 100% all the way down to the HomePage. Height does not needed height 100% all the way down to the HomePage. Height does not
have to be auto, empirically. have to be auto, empirically.

View file

@ -67,9 +67,6 @@ limitations under the License.
} }
} }
.mx_MyGroups_headerCard_header { .mx_MyGroups_headerCard_header {
font-weight: bold; font-weight: bold;
margin-bottom: 10px; margin-bottom: 10px;
@ -98,6 +95,11 @@ limitations under the License.
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow-y: auto;
}
.mx_MyGroups_scrollable {
overflow-y: inherit;
} }
.mx_MyGroups_placeholder { .mx_MyGroups_placeholder {

View file

@ -23,6 +23,7 @@ limitations under the License.
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
height: 100%;
} }
.mx_TagPanel_items_selected { .mx_TagPanel_items_selected {
@ -57,6 +58,7 @@ limitations under the License.
.mx_TagPanel .mx_TagPanel_scroller { .mx_TagPanel .mx_TagPanel_scroller {
flex-grow: 1; flex-grow: 1;
width: 100%;
} }
.mx_TagPanel .mx_TagPanel_tagTileContainer { .mx_TagPanel .mx_TagPanel_tagTileContainer {

View file

@ -14,14 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// CSS voodoo to support a gemini-scrollbar for the contents of the dialog
.mx_Dialog_unknownDevice .mx_Dialog {
// ideally we'd shrink the height to fit when needed, but in practice this
// is a pain in the ass. plus might as well make the dialog big given how
// important it is.
height: 100%;
}
.mx_UnknownDeviceDialog { .mx_UnknownDeviceDialog {
height: 100%; height: 100%;
display: flex; display: flex;
@ -44,6 +36,7 @@ limitations under the License.
.mx_UnknownDeviceDialog .mx_Dialog_content { .mx_UnknownDeviceDialog .mx_Dialog_content {
margin-bottom: 24px; margin-bottom: 24px;
overflow-y: scroll;
} }
.mx_UnknownDeviceDialog_deviceList > li { .mx_UnknownDeviceDialog_deviceList > li {

View file

@ -1,77 +0,0 @@
/*
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.
*/
.mx_SearchableEntityList {
display: flex;
flex-direction: column;
}
.mx_SearchableEntityList_query {
font-family: $font-family;
border-radius: 3px;
border: 1px solid $input-border-color;
padding: 9px;
color: $primary-fg-color;
background-color: $primary-bg-color;
margin-left: 3px;
font-size: 15px;
margin-bottom: 8px;
width: 189px;
}
.mx_SearchableEntityList_query::-moz-placeholder {
color: $primary-fg-color;
opacity: 0.5;
font-size: 12px;
}
.mx_SearchableEntityList_query::-webkit-input-placeholder {
color: $primary-fg-color;
opacity: 0.5;
font-size: 12px;
}
.mx_SearchableEntityList_listWrapper {
flex: 1;
overflow-y: auto;
}
.mx_SearchableEntityList_list {
display: table;
table-layout: fixed;
width: 100%;
}
.mx_SearchableEntityList_list .mx_EntityTile_chevron {
display: none;
}
.mx_SearchableEntityList_hrWrapper {
width: 100%;
flex: 0 0 auto;
}
.mx_SearchableEntityList hr {
height: 1px;
border: 0px;
color: $primary-fg-color;
background-color: $primary-fg-color;
margin-right: 15px;
margin-top: 11px;
margin-bottom: 11px;
}

View file

@ -219,10 +219,6 @@ $user-tile-hover-bg-color: $header-panel-bg-color;
filter: invert(1); filter: invert(1);
} }
.gm-scrollbar .thumb {
filter: invert(1);
}
// markdown overrides: // markdown overrides:
.mx_EventTile_content .markdown-body pre:hover { .mx_EventTile_content .markdown-body pre:hover {
border-color: #808080 !important; // inverted due to rules below border-color: #808080 !important; // inverted due to rules below

View file

@ -23,11 +23,11 @@ import PropTypes from 'prop-types';
import request from 'browser-request'; import request from 'browser-request';
import { _t } from '../../languageHandler'; import { _t } from '../../languageHandler';
import sanitizeHtml from 'sanitize-html'; import sanitizeHtml from 'sanitize-html';
import * as sdk from '../../index';
import dis from '../../dispatcher'; import dis from '../../dispatcher';
import {MatrixClientPeg} from '../../MatrixClientPeg'; import {MatrixClientPeg} from '../../MatrixClientPeg';
import classnames from 'classnames'; import classnames from 'classnames';
import MatrixClientContext from "../../contexts/MatrixClientContext"; import MatrixClientContext from "../../contexts/MatrixClientContext";
import AutoHideScrollbar from "./AutoHideScrollbar";
export default class EmbeddedPage extends React.PureComponent { export default class EmbeddedPage extends React.PureComponent {
static propTypes = { static propTypes = {
@ -117,10 +117,9 @@ export default class EmbeddedPage extends React.PureComponent {
</div>; </div>;
if (this.props.scrollbar) { if (this.props.scrollbar) {
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper"); return <AutoHideScrollbar className={classes}>
return <GeminiScrollbarWrapper autoshow={true} className={classes}>
{content} {content}
</GeminiScrollbarWrapper>; </AutoHideScrollbar>;
} else { } else {
return <div className={classes}> return <div className={classes}>
{content} {content}

View file

@ -39,6 +39,7 @@ import {makeGroupPermalink, makeUserPermalink} from "../../utils/permalinks/Perm
import {Group} from "matrix-js-sdk"; import {Group} from "matrix-js-sdk";
import {allSettled, sleep} from "../../utils/promise"; import {allSettled, sleep} from "../../utils/promise";
import RightPanelStore from "../../stores/RightPanelStore"; import RightPanelStore from "../../stores/RightPanelStore";
import AutoHideScrollbar from "./AutoHideScrollbar";
const LONG_DESC_PLACEHOLDER = _td( const LONG_DESC_PLACEHOLDER = _td(
`<h1>HTML for your community's page</h1> `<h1>HTML for your community's page</h1>
@ -1173,7 +1174,6 @@ export default createReactClass({
render: function() { render: function() {
const GroupAvatar = sdk.getComponent("avatars.GroupAvatar"); const GroupAvatar = sdk.getComponent("avatars.GroupAvatar");
const Spinner = sdk.getComponent("elements.Spinner"); const Spinner = sdk.getComponent("elements.Spinner");
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
if (this.state.summaryLoading && this.state.error === null || this.state.saving) { if (this.state.summaryLoading && this.state.error === null || this.state.saving) {
return <Spinner />; return <Spinner />;
@ -1332,10 +1332,10 @@ export default createReactClass({
<GroupHeaderButtons /> <GroupHeaderButtons />
</div> </div>
<MainSplit panel={rightPanel}> <MainSplit panel={rightPanel}>
<GeminiScrollbarWrapper className="mx_GroupView_body"> <AutoHideScrollbar className="mx_GroupView_body">
{ this._getMembershipSection() } { this._getMembershipSection() }
{ this._getGroupSection() } { this._getGroupSection() }
</GeminiScrollbarWrapper> </AutoHideScrollbar>
</MainSplit> </MainSplit>
</main> </main>
); );

View file

@ -22,6 +22,7 @@ import { _t } from '../../languageHandler';
import dis from '../../dispatcher'; import dis from '../../dispatcher';
import AccessibleButton from '../views/elements/AccessibleButton'; import AccessibleButton from '../views/elements/AccessibleButton';
import MatrixClientContext from "../../contexts/MatrixClientContext"; import MatrixClientContext from "../../contexts/MatrixClientContext";
import AutoHideScrollbar from "./AutoHideScrollbar";
export default createReactClass({ export default createReactClass({
displayName: 'MyGroups', displayName: 'MyGroups',
@ -62,8 +63,6 @@ export default createReactClass({
const Loader = sdk.getComponent("elements.Spinner"); const Loader = sdk.getComponent("elements.Spinner");
const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader'); const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
const GroupTile = sdk.getComponent("groups.GroupTile"); const GroupTile = sdk.getComponent("groups.GroupTile");
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
let content; let content;
let contentHeader; let contentHeader;
@ -74,7 +73,7 @@ export default createReactClass({
}); });
contentHeader = groupNodes.length > 0 ? <h3>{ _t('Your Communities') }</h3> : <div />; contentHeader = groupNodes.length > 0 ? <h3>{ _t('Your Communities') }</h3> : <div />;
content = groupNodes.length > 0 ? content = groupNodes.length > 0 ?
<GeminiScrollbarWrapper> <AutoHideScrollbar className="mx_MyGroups_scrollable">
<div className="mx_MyGroups_microcopy"> <div className="mx_MyGroups_microcopy">
<p> <p>
{ _t( { _t(
@ -93,7 +92,7 @@ export default createReactClass({
<div className="mx_MyGroups_joinedGroups"> <div className="mx_MyGroups_joinedGroups">
{ groupNodes } { groupNodes }
</div> </div>
</GeminiScrollbarWrapper> : </AutoHideScrollbar> :
<div className="mx_MyGroups_placeholder"> <div className="mx_MyGroups_placeholder">
{ _t( { _t(
"You're not currently a member of any communities.", "You're not currently a member of any communities.",

View file

@ -405,21 +405,6 @@ export default createReactClass({
this.onResize(); this.onResize();
document.addEventListener("keydown", this.onKeyDown); document.addEventListener("keydown", this.onKeyDown);
// XXX: EVIL HACK to autofocus inviting on empty rooms.
// We use the setTimeout to avoid racing with focus_composer.
if (this.state.room &&
this.state.room.getJoinedMemberCount() == 1 &&
this.state.room.getLiveTimeline() &&
this.state.room.getLiveTimeline().getEvents() &&
this.state.room.getLiveTimeline().getEvents().length <= 6) {
const inviteBox = document.getElementById("mx_SearchableEntityList_query");
setTimeout(function() {
if (inviteBox) {
inviteBox.focus();
}
}, 50);
}
}, },
shouldComponentUpdate: function(nextProps, nextState) { shouldComponentUpdate: function(nextProps, nextState) {

View file

@ -782,7 +782,7 @@ export default createReactClass({
if (!this._divScroll) { if (!this._divScroll) {
// Likewise, we should have the ref by this point, but if not // Likewise, we should have the ref by this point, but if not
// turn the NPE into something meaningful. // turn the NPE into something meaningful.
throw new Error("ScrollPanel._getScrollNode called before gemini ref collected"); throw new Error("ScrollPanel._getScrollNode called before AutoHideScrollbar ref collected");
} }
return this._divScroll; return this._divScroll;

View file

@ -28,6 +28,7 @@ import { _t } from '../../languageHandler';
import { Droppable } from 'react-beautiful-dnd'; import { Droppable } from 'react-beautiful-dnd';
import classNames from 'classnames'; import classNames from 'classnames';
import MatrixClientContext from "../../contexts/MatrixClientContext"; import MatrixClientContext from "../../contexts/MatrixClientContext";
import AutoHideScrollbar from "./AutoHideScrollbar";
const TagPanel = createReactClass({ const TagPanel = createReactClass({
displayName: 'TagPanel', displayName: 'TagPanel',
@ -106,7 +107,6 @@ const TagPanel = createReactClass({
const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
const ActionButton = sdk.getComponent('elements.ActionButton'); const ActionButton = sdk.getComponent('elements.ActionButton');
const TintableSvg = sdk.getComponent('elements.TintableSvg'); const TintableSvg = sdk.getComponent('elements.TintableSvg');
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
const tags = this.state.orderedTags.map((tag, index) => { const tags = this.state.orderedTags.map((tag, index) => {
return <DNDTagTile return <DNDTagTile
@ -138,9 +138,8 @@ const TagPanel = createReactClass({
{ clearButton } { clearButton }
</div> </div>
<div className="mx_TagPanel_divider" /> <div className="mx_TagPanel_divider" />
<GeminiScrollbarWrapper <AutoHideScrollbar
className="mx_TagPanel_scroller" className="mx_TagPanel_scroller"
autoshow={true}
// XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273 // XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273
// instead of onClick. Otherwise we experience https://github.com/vector-im/riot-web/issues/6253 // instead of onClick. Otherwise we experience https://github.com/vector-im/riot-web/issues/6253
onMouseDown={this.onMouseDown} onMouseDown={this.onMouseDown}
@ -166,7 +165,7 @@ const TagPanel = createReactClass({
</div> </div>
) } ) }
</Droppable> </Droppable>
</GeminiScrollbarWrapper> </AutoHideScrollbar>
</div>; </div>;
}, },
}); });

View file

@ -122,7 +122,6 @@ export default createReactClass({
}, },
render: function() { render: function() {
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
if (this.props.devices === null) { if (this.props.devices === null) {
const Spinner = sdk.getComponent("elements.Spinner"); const Spinner = sdk.getComponent("elements.Spinner");
return <Spinner />; return <Spinner />;
@ -168,7 +167,7 @@ export default createReactClass({
title={_t('Room contains unknown sessions')} title={_t('Room contains unknown sessions')}
contentId='mx_Dialog_content' 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> <h4>
{ _t('"%(RoomName)s" contains sessions that you haven\'t seen before.', {RoomName: this.props.room.name}) } { _t('"%(RoomName)s" contains sessions that you haven\'t seen before.', {RoomName: this.props.room.name}) }
</h4> </h4>
@ -176,7 +175,7 @@ export default createReactClass({
{ _t("Unknown sessions") }: { _t("Unknown sessions") }:
<UnknownDeviceList devices={this.props.devices} /> <UnknownDeviceList devices={this.props.devices} />
</GeminiScrollbarWrapper> </div>
<DialogButtons primaryButton={sendButtonLabel} <DialogButtons primaryButton={sendButtonLabel}
onPrimaryButtonClick={sendButtonOnClick} onPrimaryButtonClick={sendButtonOnClick}
onCancel={this._onDismissClicked} /> 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 GroupStore from '../../../stores/GroupStore';
import AccessibleButton from '../elements/AccessibleButton'; import AccessibleButton from '../elements/AccessibleButton';
import MatrixClientContext from "../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext";
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
export default createReactClass({ export default createReactClass({
displayName: 'GroupMemberInfo', displayName: 'GroupMemberInfo',
@ -182,10 +183,9 @@ export default createReactClass({
this.props.groupMember.displayname || this.props.groupMember.userId this.props.groupMember.displayname || this.props.groupMember.userId
); );
const GeminiScrollbarWrapper = sdk.getComponent('elements.GeminiScrollbarWrapper');
return ( return (
<div className="mx_MemberInfo" role="tabpanel"> <div className="mx_MemberInfo" role="tabpanel">
<GeminiScrollbarWrapper autoshow={true}> <AutoHideScrollbar>
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}> <AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" /> <img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />
</AccessibleButton> </AccessibleButton>
@ -199,7 +199,7 @@ export default createReactClass({
</div> </div>
{ adminTools } { adminTools }
</GeminiScrollbarWrapper> </AutoHideScrollbar>
</div> </div>
); );
}, },

View file

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

View file

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

View file

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

View file

@ -3902,10 +3902,6 @@ fuse.js@^2.2.0:
resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-2.7.4.tgz#96e420fde7ef011ac49c258a621314fe576536f9" resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-2.7.4.tgz#96e420fde7ef011ac49c258a621314fe576536f9"
integrity sha1-luQg/efvARrEnCWKYhMU/ldlNvk= integrity sha1-luQg/efvARrEnCWKYhMU/ldlNvk=
"gemini-scrollbar@github:matrix-org/gemini-scrollbar#91e1e566", gemini-scrollbar@matrix-org/gemini-scrollbar#91e1e566:
version "1.4.3"
resolved "https://codeload.github.com/matrix-org/gemini-scrollbar/tar.gz/91e1e566fa33324188f278801baf4a79f9f554ab"
gensync@^1.0.0-beta.1: gensync@^1.0.0-beta.1:
version "1.0.0-beta.1" version "1.0.0-beta.1"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
@ -6963,12 +6959,6 @@ react-focus-lock@^2.2.1:
use-callback-ref "^1.2.1" use-callback-ref "^1.2.1"
use-sidecar "^1.0.1" use-sidecar "^1.0.1"
"react-gemini-scrollbar@github:matrix-org/react-gemini-scrollbar#9cf17f63b7c0b0ec5f31df27da0f82f7238dc594":
version "2.1.5"
resolved "https://codeload.github.com/matrix-org/react-gemini-scrollbar/tar.gz/9cf17f63b7c0b0ec5f31df27da0f82f7238dc594"
dependencies:
gemini-scrollbar matrix-org/gemini-scrollbar#91e1e566
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0:
version "16.13.0" version "16.13.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527"