Merge branch 'experimental' into nadonomy/icons
This commit is contained in:
commit
1d43712a8f
18 changed files with 207 additions and 120 deletions
|
@ -19,14 +19,14 @@ limitations under the License.
|
||||||
each with a flex-shrink difference of 4 order of magnitude,
|
each with a flex-shrink difference of 4 order of magnitude,
|
||||||
so they ideally wouldn't affect each other.
|
so they ideally wouldn't affect each other.
|
||||||
lowest category: .mx_RoomSubList
|
lowest category: .mx_RoomSubList
|
||||||
flex:-shrink: 10000000
|
flex-shrink: 10000000
|
||||||
distribute size of items within the same categery by their size
|
distribute size of items within the same categery by their size
|
||||||
middle category: .mx_RoomSubList.resized-sized
|
middle category: .mx_RoomSubList.resized-sized
|
||||||
flex:-shrink: 1000
|
flex-shrink: 1000
|
||||||
applied when using the resizer, will have a max-height set to it,
|
applied when using the resizer, will have a max-height set to it,
|
||||||
to limit the size
|
to limit the size
|
||||||
highest category: .mx_RoomSubList.resized-all
|
highest category: .mx_RoomSubList.resized-all
|
||||||
flex:-shrink: 1
|
flex-shrink: 1
|
||||||
small flex-shrink value (1), is only added if you can drag the resizer so far
|
small flex-shrink value (1), is only added if you can drag the resizer so far
|
||||||
so in practice you can only assign this category if there is enough space.
|
so in practice you can only assign this category if there is enough space.
|
||||||
*/
|
*/
|
||||||
|
@ -39,7 +39,7 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomSubList_nonEmpty {
|
.mx_RoomSubList_nonEmpty {
|
||||||
min-height: 76px;
|
min-height: 70px;
|
||||||
|
|
||||||
.mx_AutoHideScrollbar_offset {
|
.mx_AutoHideScrollbar_offset {
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
|
@ -154,7 +154,7 @@ limitations under the License.
|
||||||
position: sticky;
|
position: sticky;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 40px;
|
height: 30px;
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
@ -162,10 +162,10 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_IndicatorScrollbar_topOverflow > .mx_AutoHideScrollbar_offset {
|
&.mx_IndicatorScrollbar_topOverflow > .mx_AutoHideScrollbar_offset {
|
||||||
margin-top: -40px;
|
margin-top: -30px;
|
||||||
}
|
}
|
||||||
&.mx_IndicatorScrollbar_bottomOverflow > .mx_AutoHideScrollbar_offset {
|
&.mx_IndicatorScrollbar_bottomOverflow > .mx_AutoHideScrollbar_offset {
|
||||||
margin-bottom: -40px;
|
margin-bottom: -30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_IndicatorScrollbar_topOverflow::before {
|
&.mx_IndicatorScrollbar_topOverflow::before {
|
||||||
|
|
|
@ -192,6 +192,8 @@ $progressbar-color: #000;
|
||||||
// it has the appearance of a text box so the controls
|
// it has the appearance of a text box so the controls
|
||||||
// appear to be part of the input
|
// appear to be part of the input
|
||||||
|
|
||||||
|
.mx_MatrixChat {
|
||||||
|
|
||||||
:not(.mx_textinput) > input[type=text],
|
:not(.mx_textinput) > input[type=text],
|
||||||
:not(.mx_textinput) > input[type=search],
|
:not(.mx_textinput) > input[type=search],
|
||||||
.mx_textinput {
|
.mx_textinput {
|
||||||
|
@ -202,22 +204,25 @@ $progressbar-color: #000;
|
||||||
color: $input-darker-fg-color;
|
color: $input-darker-fg-color;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #c1c1c1;
|
border: 1px solid #c1c1c1;
|
||||||
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_textinput {
|
.mx_textinput {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
|
||||||
|
|
||||||
.mx_textinput > input[type=text],
|
> input[type=text],
|
||||||
.mx_textinput > input[type=search] {
|
> input[type=search] {
|
||||||
border: none;
|
border: none;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
color: inherit; //from .mx_textinput
|
color: inherit; //from .mx_textinput
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
input[type=text],
|
input[type=text],
|
||||||
input[type=search] {
|
input[type=search],
|
||||||
|
input[type=password] {
|
||||||
padding: 9px;
|
padding: 9px;
|
||||||
font-family: $font-family;
|
font-family: $font-family;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
|
@ -69,6 +69,7 @@ export default class AutoHideScrollbar extends React.Component {
|
||||||
this.onOverflow = this.onOverflow.bind(this);
|
this.onOverflow = this.onOverflow.bind(this);
|
||||||
this.onUnderflow = this.onUnderflow.bind(this);
|
this.onUnderflow = this.onUnderflow.bind(this);
|
||||||
this._collectContainerRef = this._collectContainerRef.bind(this);
|
this._collectContainerRef = this._collectContainerRef.bind(this);
|
||||||
|
this._needsOverflowListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onOverflow() {
|
onOverflow() {
|
||||||
|
@ -81,36 +82,49 @@ export default class AutoHideScrollbar extends React.Component {
|
||||||
this.containerRef.classList.add("mx_AutoHideScrollbar_underflow");
|
this.containerRef.classList.add("mx_AutoHideScrollbar_underflow");
|
||||||
}
|
}
|
||||||
|
|
||||||
_collectContainerRef(ref) {
|
checkOverflow() {
|
||||||
if (ref && !this.containerRef) {
|
if (!this._needsOverflowListener) {
|
||||||
this.containerRef = ref;
|
return;
|
||||||
const needsOverflowListener =
|
|
||||||
document.body.classList.contains("mx_scrollbar_nooverlay");
|
|
||||||
|
|
||||||
if (needsOverflowListener) {
|
|
||||||
this.containerRef.addEventListener("overflow", this.onOverflow);
|
|
||||||
this.containerRef.addEventListener("underflow", this.onUnderflow);
|
|
||||||
}
|
}
|
||||||
if (ref.scrollHeight > ref.clientHeight) {
|
if (this.containerRef.scrollHeight > this.containerRef.clientHeight) {
|
||||||
this.onOverflow();
|
this.onOverflow();
|
||||||
} else {
|
} else {
|
||||||
this.onUnderflow();
|
this.onUnderflow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
this.checkOverflow();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
installBodyClassesIfNeeded();
|
||||||
|
this._needsOverflowListener =
|
||||||
|
document.body.classList.contains("mx_scrollbar_nooverlay");
|
||||||
|
if (this._needsOverflowListener) {
|
||||||
|
this.containerRef.addEventListener("overflow", this.onOverflow);
|
||||||
|
this.containerRef.addEventListener("underflow", this.onUnderflow);
|
||||||
|
}
|
||||||
|
this.checkOverflow();
|
||||||
|
}
|
||||||
|
|
||||||
|
_collectContainerRef(ref) {
|
||||||
|
if (ref && !this.containerRef) {
|
||||||
|
this.containerRef = ref;
|
||||||
|
}
|
||||||
if (this.props.wrappedRef) {
|
if (this.props.wrappedRef) {
|
||||||
this.props.wrappedRef(ref);
|
this.props.wrappedRef(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this.containerRef) {
|
if (this._needsOverflowListener && this.containerRef) {
|
||||||
this.containerRef.removeEventListener("overflow", this.onOverflow);
|
this.containerRef.removeEventListener("overflow", this.onOverflow);
|
||||||
this.containerRef.removeEventListener("underflow", this.onUnderflow);
|
this.containerRef.removeEventListener("underflow", this.onUnderflow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
installBodyClassesIfNeeded();
|
|
||||||
return (<div
|
return (<div
|
||||||
ref={this._collectContainerRef}
|
ref={this._collectContainerRef}
|
||||||
className={["mx_AutoHideScrollbar", this.props.className].join(" ")}
|
className={["mx_AutoHideScrollbar", this.props.className].join(" ")}
|
||||||
|
|
|
@ -1272,15 +1272,6 @@ export default React.createClass({
|
||||||
<TintableSvg src="img/icons-share.svg" width="16" height="16" />
|
<TintableSvg src="img/icons-share.svg" width="16" height="16" />
|
||||||
</AccessibleButton>,
|
</AccessibleButton>,
|
||||||
);
|
);
|
||||||
if (this.props.collapsedRhs) {
|
|
||||||
rightButtons.push(
|
|
||||||
<AccessibleButton className="mx_GroupHeader_button"
|
|
||||||
onClick={this._onShowRhsClick} title={_t('Show panel')} key="_maximiseButton"
|
|
||||||
>
|
|
||||||
<TintableSvg src="img/maximise.svg" width="10" height="16" />
|
|
||||||
</AccessibleButton>,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const rightPanel = !this.props.collapsedRhs ? <RightPanel groupId={this.props.groupId} /> : undefined;
|
const rightPanel = !this.props.collapsedRhs ? <RightPanel groupId={this.props.groupId} /> : undefined;
|
||||||
|
@ -1311,7 +1302,7 @@ export default React.createClass({
|
||||||
<div className="mx_GroupView_header_rightCol">
|
<div className="mx_GroupView_header_rightCol">
|
||||||
{ rightButtons }
|
{ rightButtons }
|
||||||
</div>
|
</div>
|
||||||
<GroupHeaderButtons />
|
<GroupHeaderButtons collapsedRhs={this.props.collapsedRhs} />
|
||||||
</div>
|
</div>
|
||||||
<MainSplit collapsedRhs={this.props.collapsedRhs} panel={rightPanel}>
|
<MainSplit collapsedRhs={this.props.collapsedRhs} panel={rightPanel}>
|
||||||
<GeminiScrollbarWrapper className="mx_GroupView_body">
|
<GeminiScrollbarWrapper className="mx_GroupView_body">
|
||||||
|
|
|
@ -21,41 +21,52 @@ export default class IndicatorScrollbar extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this._collectScroller = this._collectScroller.bind(this);
|
this._collectScroller = this._collectScroller.bind(this);
|
||||||
|
this._collectScrollerComponent = this._collectScrollerComponent.bind(this);
|
||||||
this.checkOverflow = this.checkOverflow.bind(this);
|
this.checkOverflow = this.checkOverflow.bind(this);
|
||||||
|
this._scrollElement = null;
|
||||||
|
this._autoHideScrollbar = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_collectScroller(scroller) {
|
_collectScroller(scroller) {
|
||||||
if (scroller && !this._scroller) {
|
if (scroller && !this._scrollElement) {
|
||||||
this._scroller = scroller;
|
this._scrollElement = scroller;
|
||||||
this._scroller.addEventListener("scroll", this.checkOverflow);
|
this._scrollElement.addEventListener("scroll", this.checkOverflow);
|
||||||
this.checkOverflow();
|
this.checkOverflow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_collectScrollerComponent(autoHideScrollbar) {
|
||||||
|
this._autoHideScrollbar = autoHideScrollbar;
|
||||||
|
}
|
||||||
|
|
||||||
checkOverflow() {
|
checkOverflow() {
|
||||||
const hasTopOverflow = this._scroller.scrollTop > 0;
|
const hasTopOverflow = this._scrollElement.scrollTop > 0;
|
||||||
const hasBottomOverflow = this._scroller.scrollHeight >
|
const hasBottomOverflow = this._scrollElement.scrollHeight >
|
||||||
(this._scroller.scrollTop + this._scroller.clientHeight);
|
(this._scrollElement.scrollTop + this._scrollElement.clientHeight);
|
||||||
if (hasTopOverflow) {
|
if (hasTopOverflow) {
|
||||||
this._scroller.classList.add("mx_IndicatorScrollbar_topOverflow");
|
this._scrollElement.classList.add("mx_IndicatorScrollbar_topOverflow");
|
||||||
} else {
|
} else {
|
||||||
this._scroller.classList.remove("mx_IndicatorScrollbar_topOverflow");
|
this._scrollElement.classList.remove("mx_IndicatorScrollbar_topOverflow");
|
||||||
}
|
}
|
||||||
if (hasBottomOverflow) {
|
if (hasBottomOverflow) {
|
||||||
this._scroller.classList.add("mx_IndicatorScrollbar_bottomOverflow");
|
this._scrollElement.classList.add("mx_IndicatorScrollbar_bottomOverflow");
|
||||||
} else {
|
} else {
|
||||||
this._scroller.classList.remove("mx_IndicatorScrollbar_bottomOverflow");
|
this._scrollElement.classList.remove("mx_IndicatorScrollbar_bottomOverflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._autoHideScrollbar) {
|
||||||
|
this._autoHideScrollbar.checkOverflow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this._scroller) {
|
if (this._scrollElement) {
|
||||||
this._scroller.removeEventListener("scroll", this.checkOverflow);
|
this._scrollElement.removeEventListener("scroll", this.checkOverflow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (<AutoHideScrollbar wrappedRef={this._collectScroller} {... this.props}>
|
return (<AutoHideScrollbar ref={this._collectScrollerComponent} wrappedRef={this._collectScroller} {... this.props}>
|
||||||
{ this.props.children }
|
{ this.props.children }
|
||||||
</AutoHideScrollbar>);
|
</AutoHideScrollbar>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ const LoggedInView = React.createClass({
|
||||||
// Called with the credentials of a registered user (if they were a ROU that
|
// Called with the credentials of a registered user (if they were a ROU that
|
||||||
// transitioned to PWLU)
|
// transitioned to PWLU)
|
||||||
onRegistered: PropTypes.func,
|
onRegistered: PropTypes.func,
|
||||||
|
collapsedRhs: PropTypes.bool,
|
||||||
teamToken: PropTypes.string,
|
teamToken: PropTypes.string,
|
||||||
|
|
||||||
// Used by the RoomView to handle joining rooms
|
// Used by the RoomView to handle joining rooms
|
||||||
|
@ -438,7 +438,7 @@ const LoggedInView = React.createClass({
|
||||||
eventPixelOffset={this.props.initialEventPixelOffset}
|
eventPixelOffset={this.props.initialEventPixelOffset}
|
||||||
key={this.props.currentRoomId || 'roomview'}
|
key={this.props.currentRoomId || 'roomview'}
|
||||||
disabled={this.props.middleDisabled}
|
disabled={this.props.middleDisabled}
|
||||||
collapsedRhs={this.props.collapseRhs}
|
collapsedRhs={this.props.collapsedRhs}
|
||||||
ConferenceHandler={this.props.ConferenceHandler}
|
ConferenceHandler={this.props.ConferenceHandler}
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
|
@ -488,7 +488,7 @@ const LoggedInView = React.createClass({
|
||||||
page_element = <GroupView
|
page_element = <GroupView
|
||||||
groupId={this.props.currentGroupId}
|
groupId={this.props.currentGroupId}
|
||||||
isNew={this.props.currentGroupIsNew}
|
isNew={this.props.currentGroupIsNew}
|
||||||
collapsedRhs={this.props.collapseRhs}
|
collapsedRhs={this.props.collapsedRhs}
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,10 +41,13 @@ export default class MainSplit extends React.Component {
|
||||||
{onResized: this._onResized},
|
{onResized: this._onResized},
|
||||||
);
|
);
|
||||||
resizer.setClassNames(classNames);
|
resizer.setClassNames(classNames);
|
||||||
const rhsSize = window.localStorage.getItem("mx_rhs_size");
|
let rhsSize = window.localStorage.getItem("mx_rhs_size");
|
||||||
if (rhsSize !== null) {
|
if (rhsSize !== null) {
|
||||||
resizer.forHandleAt(0).resize(parseInt(rhsSize, 10));
|
rhsSize = parseInt(rhsSize, 10);
|
||||||
|
} else {
|
||||||
|
rhsSize = 350;
|
||||||
}
|
}
|
||||||
|
resizer.forHandleAt(0).resize(rhsSize);
|
||||||
|
|
||||||
resizer.attach();
|
resizer.attach();
|
||||||
this.resizer = resizer;
|
this.resizer = resizer;
|
||||||
|
@ -55,7 +58,7 @@ export default class MainSplit extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (this.props.panel && !this.collapsedRhs) {
|
if (this.props.panel && !this.props.collapsedRhs) {
|
||||||
this._createResizer();
|
this._createResizer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,7 +161,7 @@ export default React.createClass({
|
||||||
viewUserId: null,
|
viewUserId: null,
|
||||||
|
|
||||||
collapseLhs: false,
|
collapseLhs: false,
|
||||||
collapseRhs: false,
|
collapsedRhs: window.localStorage.getItem("mx_rhs_collapsed") === "true",
|
||||||
leftDisabled: false,
|
leftDisabled: false,
|
||||||
middleDisabled: false,
|
middleDisabled: false,
|
||||||
rightDisabled: false,
|
rightDisabled: false,
|
||||||
|
@ -555,7 +555,7 @@ export default React.createClass({
|
||||||
break;
|
break;
|
||||||
case 'view_user':
|
case 'view_user':
|
||||||
// FIXME: ugly hack to expand the RightPanel and then re-dispatch.
|
// FIXME: ugly hack to expand the RightPanel and then re-dispatch.
|
||||||
if (this.state.collapseRhs) {
|
if (this.state.collapsedRhs) {
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'show_right_panel',
|
action: 'show_right_panel',
|
||||||
|
@ -656,13 +656,15 @@ export default React.createClass({
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'hide_right_panel':
|
case 'hide_right_panel':
|
||||||
|
window.localStorage.setItem("mx_rhs_collapsed", true);
|
||||||
this.setState({
|
this.setState({
|
||||||
collapseRhs: true,
|
collapsedRhs: true,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'show_right_panel':
|
case 'show_right_panel':
|
||||||
|
window.localStorage.setItem("mx_rhs_collapsed", false);
|
||||||
this.setState({
|
this.setState({
|
||||||
collapseRhs: false,
|
collapsedRhs: false,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'panel_disable': {
|
case 'panel_disable': {
|
||||||
|
@ -1217,7 +1219,7 @@ export default React.createClass({
|
||||||
view: VIEWS.LOGIN,
|
view: VIEWS.LOGIN,
|
||||||
ready: false,
|
ready: false,
|
||||||
collapseLhs: false,
|
collapseLhs: false,
|
||||||
collapseRhs: false,
|
collapsedRhs: false,
|
||||||
currentRoomId: null,
|
currentRoomId: null,
|
||||||
page_type: PageTypes.RoomDirectory,
|
page_type: PageTypes.RoomDirectory,
|
||||||
});
|
});
|
||||||
|
|
|
@ -110,8 +110,9 @@ const RoomSubList = React.createClass({
|
||||||
if (this.isCollapsableOnClick()) {
|
if (this.isCollapsableOnClick()) {
|
||||||
// The header isCollapsable, so the click is to be interpreted as collapse and truncation logic
|
// The header isCollapsable, so the click is to be interpreted as collapse and truncation logic
|
||||||
const isHidden = !this.state.hidden;
|
const isHidden = !this.state.hidden;
|
||||||
this.setState({hidden: isHidden});
|
this.setState({hidden: isHidden}, () => {
|
||||||
this.props.onHeaderClick(isHidden);
|
this.props.onHeaderClick(isHidden);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// The header is stuck, so the click is to be interpreted as a scroll to the header
|
// The header is stuck, so the click is to be interpreted as a scroll to the header
|
||||||
this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition);
|
this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition);
|
||||||
|
|
|
@ -56,7 +56,6 @@ module.exports = React.createClass({
|
||||||
case 'focus_room_filter':
|
case 'focus_room_filter':
|
||||||
if (this.refs.search) {
|
if (this.refs.search) {
|
||||||
this.refs.search.focus();
|
this.refs.search.focus();
|
||||||
this.refs.search.select();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -83,6 +82,10 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onFocus: function(ev) {
|
||||||
|
ev.target.select();
|
||||||
|
},
|
||||||
|
|
||||||
_clearSearch: function(source) {
|
_clearSearch: function(source) {
|
||||||
this.refs.search.value = "";
|
this.refs.search.value = "";
|
||||||
this.onChange();
|
this.onChange();
|
||||||
|
@ -108,6 +111,7 @@ module.exports = React.createClass({
|
||||||
ref="search"
|
ref="search"
|
||||||
className="mx_textinput_icon mx_textinput_search"
|
className="mx_textinput_icon mx_textinput_search"
|
||||||
value={ this.state.searchTerm }
|
value={ this.state.searchTerm }
|
||||||
|
onFocus={ this._onFocus }
|
||||||
onChange={ this.onChange }
|
onChange={ this.onChange }
|
||||||
onKeyDown={ this._onKeyDown }
|
onKeyDown={ this._onKeyDown }
|
||||||
placeholder={ _t('Filter room names') }
|
placeholder={ _t('Filter room names') }
|
||||||
|
|
|
@ -21,6 +21,7 @@ const ResizeHandle = (props) => {
|
||||||
ResizeHandle.propTypes = {
|
ResizeHandle.propTypes = {
|
||||||
vertical: PropTypes.bool,
|
vertical: PropTypes.bool,
|
||||||
reverse: PropTypes.bool,
|
reverse: PropTypes.bool,
|
||||||
|
id: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ResizeHandle;
|
export default ResizeHandle;
|
||||||
|
|
|
@ -55,23 +55,23 @@ export default class GroupHeaderButtons extends HeaderButtons {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderButtons() {
|
renderButtons() {
|
||||||
const isPhaseGroup = [
|
const groupPhases = [
|
||||||
RightPanel.Phase.GroupMemberInfo,
|
RightPanel.Phase.GroupMemberInfo,
|
||||||
RightPanel.Phase.GroupMemberList,
|
RightPanel.Phase.GroupMemberList,
|
||||||
].includes(this.state.phase);
|
];
|
||||||
const isPhaseRoom = [
|
const roomPhases = [
|
||||||
RightPanel.Phase.GroupRoomList,
|
RightPanel.Phase.GroupRoomList,
|
||||||
RightPanel.Phase.GroupRoomInfo,
|
RightPanel.Phase.GroupRoomInfo,
|
||||||
].includes(this.state.phase);
|
];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
<HeaderButton key="_groupMembersButton" title={_t('Members')} iconSrc="img/icons-people.svg"
|
<HeaderButton key="_groupMembersButton" title={_t('Members')} iconSrc="img/icons-people.svg"
|
||||||
isHighlighted={isPhaseGroup}
|
isHighlighted={this.isPhase(groupPhases)}
|
||||||
clickPhase={RightPanel.Phase.GroupMemberList}
|
clickPhase={RightPanel.Phase.GroupMemberList}
|
||||||
analytics={['Right Panel', 'Group Member List Button', 'click']}
|
analytics={['Right Panel', 'Group Member List Button', 'click']}
|
||||||
/>,
|
/>,
|
||||||
<HeaderButton key="_roomsButton" title={_t('Rooms')} iconSrc="img/icons-room-nobg.svg"
|
<HeaderButton key="_roomsButton" title={_t('Rooms')} iconSrc="img/icons-room-nobg.svg"
|
||||||
isHighlighted={isPhaseRoom}
|
isHighlighted={this.isPhase(roomPhases)}
|
||||||
clickPhase={RightPanel.Phase.GroupRoomList}
|
clickPhase={RightPanel.Phase.GroupRoomList}
|
||||||
analytics={['Right Panel', 'Group Room List Button', 'click']}
|
analytics={['Right Panel', 'Group Room List Button', 'click']}
|
||||||
/>,
|
/>,
|
||||||
|
|
|
@ -36,6 +36,7 @@ export default class HeaderButton extends React.Component {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_right_panel_phase',
|
action: 'view_right_panel_phase',
|
||||||
phase: this.props.clickPhase,
|
phase: this.props.clickPhase,
|
||||||
|
fromHeader: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import dis from '../../../dispatcher';
|
import dis from '../../../dispatcher';
|
||||||
|
|
||||||
export default class HeaderButtons extends React.Component {
|
export default class HeaderButtons extends React.Component {
|
||||||
|
@ -25,7 +26,7 @@ export default class HeaderButtons extends React.Component {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
phase: initialPhase,
|
phase: props.collapsedRhs ? null : initialPhase,
|
||||||
isUserPrivilegedInGroup: null,
|
isUserPrivilegedInGroup: null,
|
||||||
};
|
};
|
||||||
this.onAction = this.onAction.bind(this);
|
this.onAction = this.onAction.bind(this);
|
||||||
|
@ -47,13 +48,44 @@ export default class HeaderButtons extends React.Component {
|
||||||
}, extras));
|
}, extras));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isPhase(phases) {
|
||||||
|
if (this.props.collapsedRhs) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Array.isArray(phases)) {
|
||||||
|
return phases.includes(this.state.phase);
|
||||||
|
} else {
|
||||||
|
return phases === this.state.phase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onAction(payload) {
|
onAction(payload) {
|
||||||
if (payload.action === "view_right_panel_phase") {
|
if (payload.action === "view_right_panel_phase") {
|
||||||
|
// only actions coming from header buttons should collapse the right panel
|
||||||
|
if (this.state.phase === payload.phase && payload.fromHeader) {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'hide_right_panel',
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
phase: null,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (this.props.collapsedRhs && payload.fromHeader) {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'show_right_panel',
|
||||||
|
});
|
||||||
|
// emit payload again as the RightPanel didn't exist up
|
||||||
|
// till show_right_panel, just without the fromHeader flag
|
||||||
|
// as that would hide the right panel again
|
||||||
|
dis.dispatch(Object.assign({}, payload, {fromHeader: false}));
|
||||||
|
|
||||||
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: payload.phase,
|
phase: payload.phase,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// inline style as this will be swapped around in future commits
|
// inline style as this will be swapped around in future commits
|
||||||
|
@ -62,3 +94,7 @@ export default class HeaderButtons extends React.Component {
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HeaderButtons.propTypes = {
|
||||||
|
collapsedRhs: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
|
@ -46,24 +46,24 @@ export default class RoomHeaderButtons extends HeaderButtons {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderButtons() {
|
renderButtons() {
|
||||||
const isMembersPhase = [
|
const membersPhases = [
|
||||||
RightPanel.Phase.RoomMemberList,
|
RightPanel.Phase.RoomMemberList,
|
||||||
RightPanel.Phase.RoomMemberInfo,
|
RightPanel.Phase.RoomMemberInfo,
|
||||||
].includes(this.state.phase);
|
];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
<HeaderButton key="_membersButton" title={_t('Members')} iconSrc="img/feather-icons/user.svg"
|
<HeaderButton key="_membersButton" title={_t('Members')} iconSrc="img/feather-icons/user.svg"
|
||||||
isHighlighted={isMembersPhase}
|
isHighlighted={this.isPhase(membersPhases)}
|
||||||
clickPhase={RightPanel.Phase.RoomMemberList}
|
clickPhase={RightPanel.Phase.RoomMemberList}
|
||||||
analytics={['Right Panel', 'Member List Button', 'click']}
|
analytics={['Right Panel', 'Member List Button', 'click']}
|
||||||
/>,
|
/>,
|
||||||
<HeaderButton key="_filesButton" title={_t('Files')} iconSrc="img/feather-icons/files.svg"
|
<HeaderButton key="_filesButton" title={_t('Files')} iconSrc="img/feather-icons/files.svg"
|
||||||
isHighlighted={this.state.phase === RightPanel.Phase.FilePanel}
|
isHighlighted={this.isPhase(RightPanel.Phase.FilePanel)}
|
||||||
clickPhase={RightPanel.Phase.FilePanel}
|
clickPhase={RightPanel.Phase.FilePanel}
|
||||||
analytics={['Right Panel', 'File List Button', 'click']}
|
analytics={['Right Panel', 'File List Button', 'click']}
|
||||||
/>,
|
/>,
|
||||||
<HeaderButton key="_notifsButton" title={_t('Notifications')} iconSrc="img/feather-icons/notifications.svg"
|
<HeaderButton key="_notifsButton" title={_t('Notifications')} iconSrc="img/feather-icons/notifications.svg"
|
||||||
isHighlighted={this.state.phase === RightPanel.Phase.NotificationPanel}
|
isHighlighted={this.isPhase(RightPanel.Phase.NotificationPanel)}
|
||||||
clickPhase={RightPanel.Phase.NotificationPanel}
|
clickPhase={RightPanel.Phase.NotificationPanel}
|
||||||
analytics={['Right Panel', 'Notification List Button', 'click']}
|
analytics={['Right Panel', 'Notification List Button', 'click']}
|
||||||
/>,
|
/>,
|
||||||
|
|
|
@ -23,7 +23,6 @@ import sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
import dis from "../../../dispatcher";
|
|
||||||
import RateLimitedFunc from '../../../ratelimitedfunc';
|
import RateLimitedFunc from '../../../ratelimitedfunc';
|
||||||
|
|
||||||
import * as linkify from 'linkifyjs';
|
import * as linkify from 'linkifyjs';
|
||||||
|
@ -146,10 +145,6 @@ module.exports = React.createClass({
|
||||||
MatrixClientPeg.get().sendStateEvent(this.props.room.roomId, 'm.room.avatar', {url: null}, '');
|
MatrixClientPeg.get().sendStateEvent(this.props.room.roomId, 'm.room.avatar', {url: null}, '');
|
||||||
},
|
},
|
||||||
|
|
||||||
onShowRhsClick: function(ev) {
|
|
||||||
dis.dispatch({ action: 'show_right_panel' });
|
|
||||||
},
|
|
||||||
|
|
||||||
onShareRoomClick: function(ev) {
|
onShareRoomClick: function(ev) {
|
||||||
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
|
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
|
||||||
Modal.createTrackedDialog('share room dialog', '', ShareDialog, {
|
Modal.createTrackedDialog('share room dialog', '', ShareDialog, {
|
||||||
|
@ -394,14 +389,6 @@ module.exports = React.createClass({
|
||||||
</AccessibleButton>;
|
</AccessibleButton>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let rightPanelButtons;
|
|
||||||
if (this.props.collapsedRhs) {
|
|
||||||
rightPanelButtons =
|
|
||||||
<AccessibleButton className="mx_RoomHeader_button mx_RoomHeader_showPanel" onClick={this.onShowRhsClick} title={_t('Show panel')}>
|
|
||||||
<TintableSvg src="img/maximise.svg" width="10" height="16" />
|
|
||||||
</AccessibleButton>;
|
|
||||||
}
|
|
||||||
|
|
||||||
let rightRow;
|
let rightRow;
|
||||||
let manageIntegsButton;
|
let manageIntegsButton;
|
||||||
if (this.props.room && this.props.room.roomId && this.props.inRoom) {
|
if (this.props.room && this.props.room.roomId && this.props.inRoom) {
|
||||||
|
@ -419,7 +406,6 @@ module.exports = React.createClass({
|
||||||
{ manageIntegsButton }
|
{ manageIntegsButton }
|
||||||
{ forgetButton }
|
{ forgetButton }
|
||||||
{ searchButton }
|
{ searchButton }
|
||||||
{ rightPanelButtons }
|
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +419,7 @@ module.exports = React.createClass({
|
||||||
{ saveButton }
|
{ saveButton }
|
||||||
{ cancelButton }
|
{ cancelButton }
|
||||||
{ rightRow }
|
{ rightRow }
|
||||||
<RoomHeaderButtons />
|
<RoomHeaderButtons collapsedRhs={this.props.collapsedRhs} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -152,6 +152,8 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
this.subListSizes[id] = newSize;
|
this.subListSizes[id] = newSize;
|
||||||
window.localStorage.setItem("mx_roomlist_sizes", JSON.stringify(this.subListSizes));
|
window.localStorage.setItem("mx_roomlist_sizes", JSON.stringify(this.subListSizes));
|
||||||
|
// update overflow indicators
|
||||||
|
this._checkSubListsOverflow();
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
|
@ -167,12 +169,10 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
// load stored sizes
|
// load stored sizes
|
||||||
Object.entries(this.subListSizes).forEach(([id, size]) => {
|
Object.keys(this.subListSizes).forEach((key) => {
|
||||||
const handle = this.resizer.forHandleWithId(id);
|
this._restoreSubListSize(key);
|
||||||
if (handle) {
|
|
||||||
handle.resize(size);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
this._checkSubListsOverflow();
|
||||||
|
|
||||||
this.resizer.attach();
|
this.resizer.attach();
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
|
@ -181,7 +181,11 @@ module.exports = React.createClass({
|
||||||
componentDidUpdate: function(prevProps) {
|
componentDidUpdate: function(prevProps) {
|
||||||
this._repositionIncomingCallBox(undefined, false);
|
this._repositionIncomingCallBox(undefined, false);
|
||||||
if (this.props.searchFilter !== prevProps.searchFilter) {
|
if (this.props.searchFilter !== prevProps.searchFilter) {
|
||||||
Object.values(this._subListRefs).forEach(l => l.checkOverflow());
|
// restore sizes
|
||||||
|
Object.keys(this.subListSizes).forEach((key) => {
|
||||||
|
this._restoreSubListSize(key);
|
||||||
|
});
|
||||||
|
this._checkSubListsOverflow();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -354,6 +358,11 @@ module.exports = React.createClass({
|
||||||
// Do this here so as to not render every time the selected tags
|
// Do this here so as to not render every time the selected tags
|
||||||
// themselves change.
|
// themselves change.
|
||||||
selectedTags: TagOrderStore.getSelectedTags(),
|
selectedTags: TagOrderStore.getSelectedTags(),
|
||||||
|
}, () => {
|
||||||
|
// we don't need to restore any size here, do we?
|
||||||
|
// i guess we could have triggered a new group to appear
|
||||||
|
// that already an explicit size the last time it appeared ...
|
||||||
|
this._checkSubListsOverflow();
|
||||||
});
|
});
|
||||||
|
|
||||||
// this._lastRefreshRoomListTs = Date.now();
|
// this._lastRefreshRoomListTs = Date.now();
|
||||||
|
@ -485,9 +494,30 @@ module.exports = React.createClass({
|
||||||
(filter[0] === '#' && room.getAliases().some((alias) => alias.toLowerCase().startsWith(lcFilter))));
|
(filter[0] === '#' && room.getAliases().some((alias) => alias.toLowerCase().startsWith(lcFilter))));
|
||||||
},
|
},
|
||||||
|
|
||||||
_persistCollapsedState: function(key, collapsed) {
|
_handleCollapsedState: function(key, collapsed) {
|
||||||
|
// persist collapsed state
|
||||||
this.collapsedState[key] = collapsed;
|
this.collapsedState[key] = collapsed;
|
||||||
window.localStorage.setItem("mx_roomlist_collapsed", JSON.stringify(this.collapsedState));
|
window.localStorage.setItem("mx_roomlist_collapsed", JSON.stringify(this.collapsedState));
|
||||||
|
// load the persisted size configuration of the expanded sub list
|
||||||
|
if (!collapsed) {
|
||||||
|
this._restoreSubListSize(key);
|
||||||
|
}
|
||||||
|
// check overflow, as sub lists sizes have changed
|
||||||
|
// important this happens after calling resize above
|
||||||
|
this._checkSubListsOverflow();
|
||||||
|
},
|
||||||
|
|
||||||
|
_restoreSubListSize(key) {
|
||||||
|
const size = this.subListSizes[key];
|
||||||
|
const handle = this.resizer.forHandleWithId(key);
|
||||||
|
if (handle) {
|
||||||
|
handle.resize(size);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// check overflow for scroll indicator gradient
|
||||||
|
_checkSubListsOverflow() {
|
||||||
|
Object.values(this._subListRefs).forEach(l => l.checkOverflow());
|
||||||
},
|
},
|
||||||
|
|
||||||
_subListRef: function(key, ref) {
|
_subListRef: function(key, ref) {
|
||||||
|
@ -520,7 +550,7 @@ module.exports = React.createClass({
|
||||||
const {key, label, onHeaderClick, ... otherProps} = props;
|
const {key, label, onHeaderClick, ... otherProps} = props;
|
||||||
const chosenKey = key || label;
|
const chosenKey = key || label;
|
||||||
const onSubListHeaderClick = (collapsed) => {
|
const onSubListHeaderClick = (collapsed) => {
|
||||||
this._persistCollapsedState(chosenKey, collapsed);
|
this._handleCollapsedState(chosenKey, collapsed);
|
||||||
if (onHeaderClick) {
|
if (onHeaderClick) {
|
||||||
onHeaderClick(collapsed);
|
onHeaderClick(collapsed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,8 +84,10 @@ export class Resizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onMouseDown(event) {
|
_onMouseDown(event) {
|
||||||
const target = event.target;
|
// use closest in case the resize handle contains
|
||||||
if (!this._isResizeHandle(target) || target.parentElement !== this.container) {
|
// child dom nodes that can be the target
|
||||||
|
const resizeHandle = event.target && event.target.closest(`.${this.classNames.handle}`);
|
||||||
|
if (!resizeHandle || resizeHandle.parentElement !== this.container) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// prevent starting a drag operation
|
// prevent starting a drag operation
|
||||||
|
@ -96,7 +98,7 @@ export class Resizer {
|
||||||
this.container.classList.add(this.classNames.resizing);
|
this.container.classList.add(this.classNames.resizing);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {sizer, distributor} = this._createSizerAndDistributor(target);
|
const {sizer, distributor} = this._createSizerAndDistributor(resizeHandle);
|
||||||
|
|
||||||
const onMouseMove = (event) => {
|
const onMouseMove = (event) => {
|
||||||
const offset = sizer.offsetFromEvent(event);
|
const offset = sizer.offsetFromEvent(event);
|
||||||
|
|
Loading…
Reference in a new issue