mirror of
https://github.com/element-hq/element-web
synced 2024-11-23 17:56:01 +03:00
Merge pull request #2210 from matrix-org/bwindels/resizehandles
Redesign: resizeable/collapsible sections
This commit is contained in:
commit
933028120b
18 changed files with 671 additions and 159 deletions
|
@ -57,6 +57,7 @@
|
||||||
@import "./views/elements/_MemberEventListSummary.scss";
|
@import "./views/elements/_MemberEventListSummary.scss";
|
||||||
@import "./views/elements/_ProgressBar.scss";
|
@import "./views/elements/_ProgressBar.scss";
|
||||||
@import "./views/elements/_ReplyThread.scss";
|
@import "./views/elements/_ReplyThread.scss";
|
||||||
|
@import "./views/elements/_ResizeHandle.scss";
|
||||||
@import "./views/elements/_RichText.scss";
|
@import "./views/elements/_RichText.scss";
|
||||||
@import "./views/elements/_RoleButton.scss";
|
@import "./views/elements/_RoleButton.scss";
|
||||||
@import "./views/elements/_Spinner.scss";
|
@import "./views/elements/_Spinner.scss";
|
||||||
|
|
|
@ -15,32 +15,20 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_LeftPanel {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
border-right: 1px solid $panel-divider-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_LeftPanel_container {
|
.mx_LeftPanel_container {
|
||||||
display: flex;
|
display: flex;
|
||||||
/* LeftPanel 260px */
|
/* LeftPanel 260px */
|
||||||
flex: 0 0 260px;
|
min-width: 260px;
|
||||||
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_LeftPanel_container.mx_LeftPanel_container_hasTagPanel {
|
.mx_LeftPanel_container.collapsed {
|
||||||
/* TagPanel 70px + LeftPanel 260px */
|
min-width: unset;
|
||||||
flex: 0 0 330px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_LeftPanel_container_collapsed {
|
|
||||||
/* Collapsed LeftPanel 70px */
|
/* Collapsed LeftPanel 70px */
|
||||||
flex: 0 0 70px;
|
flex: 0 0 70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_LeftPanel_container_collapsed.mx_LeftPanel_container_hasTagPanel {
|
.mx_LeftPanel_container.collapsed.mx_LeftPanel_container_hasTagPanel {
|
||||||
/* TagPanel 70px + Collapsed LeftPanel 70px */
|
/* TagPanel 70px + Collapsed LeftPanel 70px */
|
||||||
flex: 0 0 140px;
|
flex: 0 0 140px;
|
||||||
}
|
}
|
||||||
|
@ -57,6 +45,15 @@ limitations under the License.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_LeftPanel {
|
||||||
|
background-color: $secondary-accent-color;
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
overflow-x: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_LeftPanel .mx_AppTile_mini {
|
.mx_LeftPanel .mx_AppTile_mini {
|
||||||
height: 132px;
|
height: 132px;
|
||||||
}
|
}
|
||||||
|
@ -70,7 +67,7 @@ limitations under the License.
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_LeftPanel.collapsed .mx_BottomLeftMenu {
|
.mx_LeftPanel_container.collapsed .mx_BottomLeftMenu {
|
||||||
flex: 0 0 160px;
|
flex: 0 0 160px;
|
||||||
margin-bottom: 9px;
|
margin-bottom: 9px;
|
||||||
}
|
}
|
||||||
|
@ -93,7 +90,7 @@ limitations under the License.
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed .mx_RoleButton {
|
.mx_LeftPanel_container.collapsed .mx_RoleButton {
|
||||||
margin-right: 0px ! important;
|
margin-right: 0px ! important;
|
||||||
padding-top: 3px ! important;
|
padding-top: 3px ! important;
|
||||||
padding-bottom: 3px ! important;
|
padding-bottom: 3px ! important;
|
||||||
|
@ -117,7 +114,7 @@ limitations under the License.
|
||||||
margin-right: 0px;
|
margin-right: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_LeftPanel.collapsed .mx_BottomLeftMenu_settings {
|
.mx_LeftPanel_container.collapsed .mx_BottomLeftMenu_settings {
|
||||||
float: none;
|
float: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +123,7 @@ limitations under the License.
|
||||||
flex: 0 0 50px;
|
flex: 0 0 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_LeftPanel.collapsed .mx_BottomLeftMenu {
|
.mx_LeftPanel_container.collapsed .mx_BottomLeftMenu {
|
||||||
flex: 0 0 160px;
|
flex: 0 0 160px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,19 +68,7 @@ limitations under the License.
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MatrixChat .mx_LeftPanel {
|
|
||||||
order: 1;
|
|
||||||
background-color: $secondary-accent-color;
|
|
||||||
flex: 0 0 260px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MatrixChat .mx_LeftPanel.collapsed {
|
|
||||||
flex: 0 0 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MatrixChat .mx_MatrixChat_middlePanel {
|
.mx_MatrixChat .mx_MatrixChat_middlePanel {
|
||||||
order: 2;
|
|
||||||
|
|
||||||
background-color: $primary-bg-color;
|
background-color: $primary-bg-color;
|
||||||
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -100,13 +88,3 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MatrixChat .mx_RightPanel {
|
|
||||||
order: 3;
|
|
||||||
|
|
||||||
flex: 0 0 235px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MatrixChat .mx_RightPanel.collapsed {
|
|
||||||
flex: 0 0 122px;
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,8 +15,10 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_RightPanel {
|
.mx_RightPanel {
|
||||||
|
overflow-x: hidden;
|
||||||
|
flex: 0 0 auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
min-width: 250px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_RoomSubList_labelContainer {
|
.mx_RoomSubList_labelContainer {
|
||||||
height: 31px; /* mx_RoomSubList_label height including border */
|
height: 31px; /* mx_RoomSubList_label height including border */
|
||||||
width: 235px; /* LHS Panel width */
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +38,6 @@ limitations under the License.
|
||||||
color: $roomsublist-label-fg-color;
|
color: $roomsublist-label-fg-color;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
width: 203px; /* padding + width = LHS Panel width */
|
|
||||||
height: 19px; /* height + padding = 31px = mx_RoomSubList_label height */
|
height: 19px; /* height + padding = 31px = mx_RoomSubList_label height */
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
padding-left: 16px; /* gutter */
|
padding-left: 16px; /* gutter */
|
||||||
|
@ -57,15 +55,6 @@ limitations under the License.
|
||||||
/* pointer-events: none; */
|
/* pointer-events: none; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed .mx_RoomSubList_label {
|
|
||||||
height: 17px;
|
|
||||||
width: 28px; /* collapsed LHS Panel width */
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapsed .mx_RoomSubList_labelContainer {
|
|
||||||
width: 28px; /* collapsed LHS Panel width */
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomSubList_roomCount {
|
.mx_RoomSubList_roomCount {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
@ -75,10 +64,6 @@ limitations under the License.
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed .mx_RoomSubList_roomCount {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomSubList_badge {
|
.mx_RoomSubList_badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
min-width: 15px;
|
min-width: 15px;
|
||||||
|
@ -101,12 +86,6 @@ limitations under the License.
|
||||||
filter: brightness($focus-brightness);
|
filter: brightness($focus-brightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
.collapsed .mx_RoomSubList_badge {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
.mx_RoomSubList_badgeHighlight {
|
.mx_RoomSubList_badgeHighlight {
|
||||||
background-color: $warning-color;
|
background-color: $warning-color;
|
||||||
}
|
}
|
||||||
|
@ -123,11 +102,6 @@ limitations under the License.
|
||||||
border-right: 7px solid transparent;
|
border-right: 7px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide the bottom of speech bubble */
|
|
||||||
.collapsed .mx_RoomSubList_badgeHighlight:after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomSubList_chevron {
|
.mx_RoomSubList_chevron {
|
||||||
left: 0px;
|
left: 0px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
@ -165,10 +139,6 @@ limitations under the License.
|
||||||
background-color: $secondary-accent-color;
|
background-color: $secondary-accent-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed .mx_RoomSubList_ellipsis {
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomSubList_line {
|
.mx_RoomSubList_line {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 159px;
|
width: 159px;
|
||||||
|
@ -176,10 +146,6 @@ limitations under the License.
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed .mx_RoomSubList_line {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomSubList_more {
|
.mx_RoomSubList_more {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
@ -193,10 +159,6 @@ limitations under the License.
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed .mx_RoomSubList_more {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomSubList_moreBadge {
|
.mx_RoomSubList_moreBadge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
min-width: 15px;
|
min-width: 15px;
|
||||||
|
@ -233,12 +195,6 @@ limitations under the License.
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed .mx_RoomSubList_moreBadge {
|
|
||||||
position: static;
|
|
||||||
margin-left: 16px;
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomSubList_ellipsis .mx_RoomSubList_chevronDown {
|
.mx_RoomSubList_ellipsis .mx_RoomSubList_chevronDown {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 4px;
|
top: 4px;
|
||||||
|
@ -246,3 +202,40 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.collapsed {
|
||||||
|
.mx_RoomSubList_label {
|
||||||
|
height: 17px;
|
||||||
|
width: 28px; /* collapsed LHS Panel width */
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSubList_labelContainer {
|
||||||
|
width: 28px; /* collapsed LHS Panel width */
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSubList_roomCount {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide the bottom of speech bubble */
|
||||||
|
.mx_RoomSubList_badgeHighlight:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSubList_line {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSubList_moreBadge {
|
||||||
|
position: static;
|
||||||
|
margin-left: 16px;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSubList_ellipsis {
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSubList_more {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
32
res/css/views/elements/_ResizeHandle.scss
Normal file
32
res/css/views/elements/_ResizeHandle.scss
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_ResizeHandle {
|
||||||
|
cursor: row-resize;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
background: $panel-divider-color;
|
||||||
|
padding: 1px
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ResizeHandle.mx_ResizeHandle_horizontal {
|
||||||
|
width: 1px;
|
||||||
|
cursor: col-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ResizeHandle.mx_ResizeHandle_vertical {
|
||||||
|
height: 1px;
|
||||||
|
cursor: row-resize;
|
||||||
|
}
|
|
@ -15,12 +15,13 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_RoomTile {
|
.mx_RoomTile {
|
||||||
position: relative;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
|
||||||
height: 40px;
|
height: 40px;
|
||||||
margin: 0px 9px 0px 9px;
|
margin: 0px 3px;
|
||||||
|
position: relative;
|
||||||
background-color: $secondary-accent-color;
|
background-color: $secondary-accent-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,26 +32,18 @@ limitations under the License.
|
||||||
left: -12px;
|
left: -12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.mx_RoomTile_nameContainer {
|
|
||||||
display: inline-block;
|
|
||||||
width: 180px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomTile_avatar_container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomTile_avatar {
|
.mx_RoomTile_avatar {
|
||||||
display: inline-block;
|
flex: 0;
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
padding-left: 14px;
|
padding-left: 14px;
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
vertical-align: middle;
|
}
|
||||||
|
|
||||||
|
.mx_RoomTile_avatar_container {
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomTile_dm {
|
.mx_RoomTile_dm {
|
||||||
|
@ -62,19 +55,13 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomTile_name {
|
.mx_RoomTile_name {
|
||||||
display: inline-block;
|
flex: 1 5 auto;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
position: relative;
|
padding: 6px;
|
||||||
width: 165px;
|
|
||||||
vertical-align: middle;
|
|
||||||
padding-left: 6px;
|
|
||||||
padding-right: 6px;
|
|
||||||
padding-top: 2px;
|
|
||||||
padding-bottom: 3px;
|
|
||||||
color: $roomtile-name-color;
|
color: $roomtile-name-color;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow-x: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,25 +69,30 @@ limitations under the License.
|
||||||
/* color: rgba(69, 69, 69, 0.5); */
|
/* color: rgba(69, 69, 69, 0.5); */
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed .mx_RoomTile_nameContainer {
|
.collapsed {
|
||||||
width: 60px; /* colapsed panel width */
|
.mx_RoomTile_name {
|
||||||
}
|
|
||||||
|
|
||||||
.collapsed .mx_RoomTile_name {
|
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed .mx_RoomTile_badge {
|
.mx_RoomTile_badge {
|
||||||
top: 0px;
|
|
||||||
min-width: 12px;
|
min-width: 12px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
padding: 0px 4px 0px 4px;
|
padding: 0px 4px 0px 4px;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide the bottom of speech bubble */
|
/* Hide the bottom of speech bubble */
|
||||||
.collapsed .mx_RoomTile_highlight .mx_RoomTile_badge:after {
|
.mx_RoomTile_highlight .mx_RoomTile_badge:after {
|
||||||
display: none;
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomTile_badge {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
height: 15px;
|
||||||
|
right: 5px;
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is the bottom of the speech bubble */
|
/* This is the bottom of the speech bubble */
|
||||||
|
@ -116,12 +108,8 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomTile_badge {
|
.mx_RoomTile_badge {
|
||||||
display: inline-block;
|
flex: 0 1 content;
|
||||||
min-width: 15px;
|
min-width: 15px;
|
||||||
height: 15px;
|
|
||||||
position: absolute;
|
|
||||||
right: 8px; /*gutter */
|
|
||||||
top: 9px;
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: $accent-fg-color;
|
color: $accent-fg-color;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
|
@ -155,7 +155,7 @@ class Tinter {
|
||||||
|
|
||||||
tint(primaryColor, secondaryColor, tertiaryColor) {
|
tint(primaryColor, secondaryColor, tertiaryColor) {
|
||||||
return;
|
return;
|
||||||
|
// eslint-disable-next-line no-unreachable
|
||||||
this.currentTint[0] = primaryColor;
|
this.currentTint[0] = primaryColor;
|
||||||
this.currentTint[1] = secondaryColor;
|
this.currentTint[1] = secondaryColor;
|
||||||
this.currentTint[2] = tertiaryColor;
|
this.currentTint[2] = tertiaryColor;
|
||||||
|
|
|
@ -192,20 +192,13 @@ var LeftPanel = React.createClass({
|
||||||
topBox = <SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } />;
|
topBox = <SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } />;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
const classes = classNames(
|
|
||||||
"mx_LeftPanel",
|
|
||||||
{
|
|
||||||
"collapsed": this.props.collapsed,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const tagPanelEnabled = !SettingsStore.getValue("TagPanel.disableTagPanel");
|
const tagPanelEnabled = !SettingsStore.getValue("TagPanel.disableTagPanel");
|
||||||
const tagPanel = tagPanelEnabled ? <TagPanel /> : <div />;
|
const tagPanel = tagPanelEnabled ? <TagPanel /> : <div />;
|
||||||
|
|
||||||
const containerClasses = classNames(
|
const containerClasses = classNames(
|
||||||
"mx_LeftPanel_container", "mx_fadable",
|
"mx_LeftPanel_container", "mx_fadable",
|
||||||
{
|
{
|
||||||
"mx_LeftPanel_container_collapsed": this.props.collapsed,
|
"collapsed": this.props.collapsed,
|
||||||
"mx_LeftPanel_container_hasTagPanel": tagPanelEnabled,
|
"mx_LeftPanel_container_hasTagPanel": tagPanelEnabled,
|
||||||
"mx_fadable_faded": this.props.disabled,
|
"mx_fadable_faded": this.props.disabled,
|
||||||
},
|
},
|
||||||
|
@ -214,7 +207,7 @@ var LeftPanel = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className={containerClasses}>
|
<div className={containerClasses}>
|
||||||
{ tagPanel }
|
{ tagPanel }
|
||||||
<aside className={classes} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
|
<aside className={"mx_LeftPanel"} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
|
||||||
{ topBox }
|
{ topBox }
|
||||||
<CallPreview ConferenceHandler={VectorConferenceHandler} />
|
<CallPreview ConferenceHandler={VectorConferenceHandler} />
|
||||||
<RoomList
|
<RoomList
|
||||||
|
|
|
@ -34,7 +34,8 @@ import RoomListStore from "../../stores/RoomListStore";
|
||||||
|
|
||||||
import TagOrderActions from '../../actions/TagOrderActions';
|
import TagOrderActions from '../../actions/TagOrderActions';
|
||||||
import RoomListActions from '../../actions/RoomListActions';
|
import RoomListActions from '../../actions/RoomListActions';
|
||||||
|
import ResizeHandle from '../views/elements/ResizeHandle';
|
||||||
|
import {Resizer, CollapseDistributor} from '../../resizer'
|
||||||
// We need to fetch each pinned message individually (if we don't already have it)
|
// We need to fetch each pinned message individually (if we don't already have it)
|
||||||
// so each pinned message may trigger a request. Limit the number per room for sanity.
|
// so each pinned message may trigger a request. Limit the number per room for sanity.
|
||||||
// NB. this is just for server notices rather than pinned messages in general.
|
// NB. this is just for server notices rather than pinned messages in general.
|
||||||
|
@ -91,6 +92,12 @@ const LoggedInView = React.createClass({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
this.resizer = this._createResizer();
|
||||||
|
this.resizer.attach();
|
||||||
|
this._loadResizerPreferences();
|
||||||
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
// stash the MatrixClient in case we log out before we are unmounted
|
// stash the MatrixClient in case we log out before we are unmounted
|
||||||
this._matrixClient = this.props.matrixClient;
|
this._matrixClient = this.props.matrixClient;
|
||||||
|
@ -120,6 +127,7 @@ const LoggedInView = React.createClass({
|
||||||
if (this._sessionStoreToken) {
|
if (this._sessionStoreToken) {
|
||||||
this._sessionStoreToken.remove();
|
this._sessionStoreToken.remove();
|
||||||
}
|
}
|
||||||
|
this.resizer.detach();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Child components assume that the client peg will not be null, so give them some
|
// Child components assume that the client peg will not be null, so give them some
|
||||||
|
@ -145,6 +153,49 @@ const LoggedInView = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_createResizer() {
|
||||||
|
const classNames = {
|
||||||
|
handle: "mx_ResizeHandle",
|
||||||
|
vertical: "mx_ResizeHandle_vertical",
|
||||||
|
reverse: "mx_ResizeHandle_reverse"
|
||||||
|
};
|
||||||
|
const collapseConfig = {
|
||||||
|
toggleSize: 260 - 50,
|
||||||
|
onCollapsed: (collapsed, item) => {
|
||||||
|
if (item.classList.contains("mx_LeftPanel_container")) {
|
||||||
|
this.setState({collapseLhs: collapsed});
|
||||||
|
if (collapsed) {
|
||||||
|
window.localStorage.setItem("mx_lhs_size", '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onResized: (size, item) => {
|
||||||
|
if (item.classList.contains("mx_LeftPanel_container")) {
|
||||||
|
window.localStorage.setItem("mx_lhs_size", '' + size);
|
||||||
|
} else if(item.classList.contains("mx_RightPanel")) {
|
||||||
|
window.localStorage.setItem("mx_rhs_size", '' + size);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const resizer = new Resizer(
|
||||||
|
this.resizeContainer,
|
||||||
|
CollapseDistributor,
|
||||||
|
collapseConfig);
|
||||||
|
resizer.setClassNames(classNames);
|
||||||
|
return resizer;
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadResizerPreferences() {
|
||||||
|
const lhsSize = window.localStorage.getItem("mx_lhs_size");
|
||||||
|
if (lhsSize !== null) {
|
||||||
|
this.resizer.forHandleAt(0).resize(parseInt(lhsSize, 10));
|
||||||
|
}
|
||||||
|
const rhsSize = window.localStorage.getItem("mx_rhs_size");
|
||||||
|
if (rhsSize !== null) {
|
||||||
|
this.resizer.forHandleAt(1).resize(parseInt(rhsSize, 10));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onAccountData: function(event) {
|
onAccountData: function(event) {
|
||||||
if (event.getType() === "im.vector.web.settings") {
|
if (event.getType() === "im.vector.web.settings") {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -361,6 +412,10 @@ const LoggedInView = React.createClass({
|
||||||
this.setState({mouseDown: null});
|
this.setState({mouseDown: null});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_setResizeContainerRef(div) {
|
||||||
|
this.resizeContainer = div;
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const LeftPanel = sdk.getComponent('structures.LeftPanel');
|
const LeftPanel = sdk.getComponent('structures.LeftPanel');
|
||||||
const RightPanel = sdk.getComponent('structures.RightPanel');
|
const RightPanel = sdk.getComponent('structures.RightPanel');
|
||||||
|
@ -507,14 +562,16 @@ const LoggedInView = React.createClass({
|
||||||
<div className='mx_MatrixChat_wrapper' aria-hidden={this.props.hideToSRUsers} onMouseDown={this._onMouseDown} onMouseUp={this._onMouseUp}>
|
<div className='mx_MatrixChat_wrapper' aria-hidden={this.props.hideToSRUsers} onMouseDown={this._onMouseDown} onMouseUp={this._onMouseUp}>
|
||||||
{ topBar }
|
{ topBar }
|
||||||
<DragDropContext onDragEnd={this._onDragEnd}>
|
<DragDropContext onDragEnd={this._onDragEnd}>
|
||||||
<div className={bodyClasses}>
|
<div ref={this._setResizeContainerRef} className={bodyClasses}>
|
||||||
<LeftPanel
|
<LeftPanel
|
||||||
collapsed={this.props.collapseLhs || false}
|
collapsed={this.props.collapseLhs || this.state.collapseLhs || false}
|
||||||
disabled={this.props.leftDisabled}
|
disabled={this.props.leftDisabled}
|
||||||
/>
|
/>
|
||||||
|
<ResizeHandle/>
|
||||||
<main className='mx_MatrixChat_middlePanel'>
|
<main className='mx_MatrixChat_middlePanel'>
|
||||||
{ page_element }
|
{ page_element }
|
||||||
</main>
|
</main>
|
||||||
|
<ResizeHandle reverse={true}/>
|
||||||
{ right_panel }
|
{ right_panel }
|
||||||
</div>
|
</div>
|
||||||
</DragDropContext>
|
</DragDropContext>
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../index';
|
import sdk from '../../index';
|
||||||
import dis from '../../dispatcher';
|
|
||||||
|
|
||||||
class TopLeftMenu extends React.Component {
|
class TopLeftMenu extends React.Component {
|
||||||
|
|
||||||
|
@ -30,7 +29,7 @@ class TopLeftMenu extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||||
const avatarHeight = 28;
|
const avatarHeight = 28;
|
||||||
const name = "My stuff"
|
const name = "My stuff";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_TopLeftMenu">
|
<div className="mx_TopLeftMenu">
|
||||||
|
@ -43,7 +42,7 @@ class TopLeftMenu extends React.Component {
|
||||||
<div className="mx_TopLeftMenu_name">
|
<div className="mx_TopLeftMenu_name">
|
||||||
{ name }
|
{ name }
|
||||||
</div>
|
</div>
|
||||||
<img className="mx_TopLeftMenu_chevron" src="img/topleft-chevron.svg" width="11" height="6"/>
|
<img className="mx_TopLeftMenu_chevron" src="img/topleft-chevron.svg" width="11" height="6" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
26
src/components/views/elements/ResizeHandle.js
Normal file
26
src/components/views/elements/ResizeHandle.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
|
||||||
|
import React from 'react'; // eslint-disable-line no-unused-vars
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
//see src/resizer for the actual resizing code, this is just the DOM for the resize handle
|
||||||
|
const ResizeHandle = (props) => {
|
||||||
|
const classNames = ['mx_ResizeHandle'];
|
||||||
|
if (props.vertical) {
|
||||||
|
classNames.push('mx_ResizeHandle_vertical');
|
||||||
|
} else {
|
||||||
|
classNames.push('mx_ResizeHandle_horizontal');
|
||||||
|
}
|
||||||
|
if (props.reverse) {
|
||||||
|
classNames.push('mx_ResizeHandle_reverse');
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={classNames.join(' ')} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ResizeHandle.propTypes = {
|
||||||
|
vertical: PropTypes.bool,
|
||||||
|
reverse: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ResizeHandle;
|
|
@ -337,10 +337,8 @@ module.exports = React.createClass({
|
||||||
{ dmIndicator }
|
{ dmIndicator }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_RoomTile_nameContainer">
|
|
||||||
{ label }
|
{ label }
|
||||||
{ badge }
|
{ badge }
|
||||||
</div>
|
|
||||||
{ /* { incomingCallBox } */ }
|
{ /* { incomingCallBox } */ }
|
||||||
{ tooltip }
|
{ tooltip }
|
||||||
</AccessibleButton>;
|
</AccessibleButton>;
|
||||||
|
|
128
src/resizer/distributors.js
Normal file
128
src/resizer/distributors.js
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
distributors translate a moving cursor into
|
||||||
|
CSS/DOM changes by calling the sizer
|
||||||
|
|
||||||
|
they have one method, `resize` that receives
|
||||||
|
the offset from the container edge of where
|
||||||
|
the mouse cursor is.
|
||||||
|
*/
|
||||||
|
class FixedDistributor {
|
||||||
|
constructor(sizer, item, config) {
|
||||||
|
this.sizer = sizer;
|
||||||
|
this.item = item;
|
||||||
|
this.beforeOffset = sizer.getItemOffset(this.item);
|
||||||
|
this.onResized = config.onResized;
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(offset) {
|
||||||
|
const itemSize = offset - this.beforeOffset;
|
||||||
|
this.sizer.setItemSize(this.item, itemSize);
|
||||||
|
if (this.onResized) {
|
||||||
|
this.onResized(itemSize, this.item);
|
||||||
|
}
|
||||||
|
return itemSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
sizeFromOffset(offset) {
|
||||||
|
return offset - this.beforeOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CollapseDistributor extends FixedDistributor {
|
||||||
|
constructor(sizer, item, config) {
|
||||||
|
super(sizer, item, config);
|
||||||
|
this.toggleSize = config && config.toggleSize;
|
||||||
|
this.onCollapsed = config && config.onCollapsed;
|
||||||
|
this.isCollapsed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(offset) {
|
||||||
|
const newSize = this.sizeFromOffset(offset);
|
||||||
|
const isCollapsedSize = newSize < this.toggleSize;
|
||||||
|
if (isCollapsedSize && !this.isCollapsed) {
|
||||||
|
this.isCollapsed = true;
|
||||||
|
if (this.onCollapsed) {
|
||||||
|
this.onCollapsed(true, this.item);
|
||||||
|
}
|
||||||
|
} else if (!isCollapsedSize && this.isCollapsed) {
|
||||||
|
if (this.onCollapsed) {
|
||||||
|
this.onCollapsed(false, this.item);
|
||||||
|
}
|
||||||
|
this.isCollapsed = false;
|
||||||
|
}
|
||||||
|
if (!isCollapsedSize) {
|
||||||
|
super.resize(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PercentageDistributor {
|
||||||
|
|
||||||
|
constructor(sizer, item, _config, items, container) {
|
||||||
|
this.container = container;
|
||||||
|
this.totalSize = sizer.getTotalSize();
|
||||||
|
this.sizer = sizer;
|
||||||
|
|
||||||
|
const itemIndex = items.indexOf(item);
|
||||||
|
this.beforeItems = items.slice(0, itemIndex);
|
||||||
|
this.afterItems = items.slice(itemIndex);
|
||||||
|
const percentages = PercentageDistributor._getPercentages(sizer, items);
|
||||||
|
this.beforePercentages = percentages.slice(0, itemIndex);
|
||||||
|
this.afterPercentages = percentages.slice(itemIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(offset) {
|
||||||
|
const percent = offset / this.totalSize;
|
||||||
|
const beforeSum =
|
||||||
|
this.beforePercentages.reduce((total, p) => total + p, 0);
|
||||||
|
const beforePercentages =
|
||||||
|
this.beforePercentages.map(p => (p / beforeSum) * percent);
|
||||||
|
const afterSum =
|
||||||
|
this.afterPercentages.reduce((total, p) => total + p, 0);
|
||||||
|
const afterPercentages =
|
||||||
|
this.afterPercentages.map(p => (p / afterSum) * (1 - percent));
|
||||||
|
|
||||||
|
this.beforeItems.forEach((item, index) => {
|
||||||
|
this.sizer.setItemPercentage(item, beforePercentages[index]);
|
||||||
|
});
|
||||||
|
this.afterItems.forEach((item, index) => {
|
||||||
|
this.sizer.setItemPercentage(item, afterPercentages[index]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static _getPercentages(sizer, items) {
|
||||||
|
const percentages = items.map(i => sizer.getItemPercentage(i));
|
||||||
|
const setPercentages = percentages.filter(p => p !== null);
|
||||||
|
const unsetCount = percentages.length - setPercentages.length;
|
||||||
|
const setTotal = setPercentages.reduce((total, p) => total + p, 0);
|
||||||
|
const implicitPercentage = (1 - setTotal) / unsetCount;
|
||||||
|
return percentages.map(p => p === null ? implicitPercentage : p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static setPercentage(el, percent) {
|
||||||
|
el.style.flexGrow = Math.round(percent * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
FixedDistributor,
|
||||||
|
CollapseDistributor,
|
||||||
|
PercentageDistributor,
|
||||||
|
};
|
27
src/resizer/index.js
Normal file
27
src/resizer/index.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
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 {Sizer} from "./sizer";
|
||||||
|
import {FixedDistributor, CollapseDistributor, PercentageDistributor} from "./distributors";
|
||||||
|
import {Resizer} from "./resizer";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Resizer,
|
||||||
|
Sizer,
|
||||||
|
FixedDistributor,
|
||||||
|
CollapseDistributor,
|
||||||
|
PercentageDistributor,
|
||||||
|
};
|
138
src/resizer/resizer.js
Normal file
138
src/resizer/resizer.js
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
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 {Sizer} from "./sizer";
|
||||||
|
|
||||||
|
/*
|
||||||
|
classNames:
|
||||||
|
// class on resize-handle
|
||||||
|
handle: string
|
||||||
|
// class on resize-handle
|
||||||
|
reverse: string
|
||||||
|
// class on resize-handle
|
||||||
|
vertical: string
|
||||||
|
// class on container
|
||||||
|
resizing: string
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class Resizer {
|
||||||
|
constructor(container, distributorCtor, distributorCfg, sizerCtor = Sizer) {
|
||||||
|
this.container = container;
|
||||||
|
this.distributorCtor = distributorCtor;
|
||||||
|
this.distributorCfg = distributorCfg;
|
||||||
|
this.sizerCtor = sizerCtor;
|
||||||
|
this.classNames = {
|
||||||
|
handle: "resizer-handle",
|
||||||
|
reverse: "resizer-reverse",
|
||||||
|
vertical: "resizer-vertical",
|
||||||
|
resizing: "resizer-resizing",
|
||||||
|
};
|
||||||
|
this._onMouseDown = this._onMouseDown.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
setClassNames(classNames) {
|
||||||
|
this.classNames = classNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
attach() {
|
||||||
|
this.container.addEventListener("mousedown", this._onMouseDown, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
detach() {
|
||||||
|
this.container.removeEventListener("mousedown", this._onMouseDown, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gives the distributor for a specific resize handle, as if you would have started
|
||||||
|
to drag that handle. Can be used to manipulate the size of an item programmatically.
|
||||||
|
@param {number} handleIndex the index of the resize handle in the container
|
||||||
|
@return {Distributor} a new distributor for the given handle
|
||||||
|
*/
|
||||||
|
forHandleAt(handleIndex) {
|
||||||
|
const handles = this._getResizeHandles();
|
||||||
|
const handle = handles[handleIndex];
|
||||||
|
const {distributor} = this._createSizerAndDistributor(handle);
|
||||||
|
return distributor;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isResizeHandle(el) {
|
||||||
|
return el && el.classList.contains(this.classNames.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onMouseDown(event) {
|
||||||
|
const target = event.target;
|
||||||
|
if (!this._isResizeHandle(target) || target.parentElement !== this.container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// prevent starting a drag operation
|
||||||
|
event.preventDefault();
|
||||||
|
// mark as currently resizing
|
||||||
|
if (this.classNames.resizing) {
|
||||||
|
this.container.classList.add(this.classNames.resizing);
|
||||||
|
}
|
||||||
|
|
||||||
|
const {sizer, distributor} = this._createSizerAndDistributor(target);
|
||||||
|
|
||||||
|
const onMouseMove = (event) => {
|
||||||
|
const offset = sizer.offsetFromEvent(event);
|
||||||
|
distributor.resize(offset);
|
||||||
|
};
|
||||||
|
|
||||||
|
const body = document.body;
|
||||||
|
const onMouseUp = (event) => {
|
||||||
|
if (this.classNames.resizing) {
|
||||||
|
this.container.classList.remove(this.classNames.resizing);
|
||||||
|
}
|
||||||
|
body.removeEventListener("mouseup", onMouseUp, false);
|
||||||
|
body.removeEventListener("mousemove", onMouseMove, false);
|
||||||
|
};
|
||||||
|
body.addEventListener("mouseup", onMouseUp, false);
|
||||||
|
body.addEventListener("mousemove", onMouseMove, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_createSizerAndDistributor(resizeHandle) {
|
||||||
|
const vertical = resizeHandle.classList.contains(this.classNames.vertical);
|
||||||
|
const reverse = resizeHandle.classList.contains(this.classNames.reverse);
|
||||||
|
|
||||||
|
// eslint-disable-next-line new-cap
|
||||||
|
const sizer = new this.sizerCtor(this.container, vertical, reverse);
|
||||||
|
|
||||||
|
const items = this._getResizableItems();
|
||||||
|
const prevItem = resizeHandle.previousElementSibling;
|
||||||
|
// if reverse, resize the item after the handle instead of before, so + 1
|
||||||
|
const itemIndex = items.indexOf(prevItem) + (reverse ? 1 : 0);
|
||||||
|
const item = items[itemIndex];
|
||||||
|
// eslint-disable-next-line new-cap
|
||||||
|
const distributor = new this.distributorCtor(
|
||||||
|
sizer, item, this.distributorCfg,
|
||||||
|
items, this.container);
|
||||||
|
return {sizer, distributor};
|
||||||
|
}
|
||||||
|
|
||||||
|
_getResizableItems() {
|
||||||
|
return Array.from(this.container.children).filter(el => {
|
||||||
|
return !this._isResizeHandle(el) && (
|
||||||
|
this._isResizeHandle(el.previousElementSibling) ||
|
||||||
|
this._isResizeHandle(el.nextElementSibling));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_getResizeHandles() {
|
||||||
|
return Array.from(this.container.children).filter(el => {
|
||||||
|
return this._isResizeHandle(el);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
55
src/resizer/room.js
Normal file
55
src/resizer/room.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
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 {Sizer} from "./sizer";
|
||||||
|
import {FixedDistributor} from "./distributors";
|
||||||
|
|
||||||
|
class RoomSizer extends Sizer {
|
||||||
|
setItemSize(item, size) {
|
||||||
|
const isString = typeof size === "string";
|
||||||
|
const cl = item.classList;
|
||||||
|
if (isString) {
|
||||||
|
item.style.flex = null;
|
||||||
|
if (size === "show-content") {
|
||||||
|
cl.add("show-content");
|
||||||
|
cl.remove("show-available");
|
||||||
|
item.style.maxHeight = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cl.add("show-available");
|
||||||
|
//item.style.flex = `0 1 ${Math.round(size)}px`;
|
||||||
|
item.style.maxHeight = `${Math.round(size)}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class RoomDistributor extends FixedDistributor {
|
||||||
|
resize(offset) {
|
||||||
|
const itemSize = offset - this.sizer.getItemOffset(this.item);
|
||||||
|
|
||||||
|
if (itemSize > this.item.scrollHeight) {
|
||||||
|
this.sizer.setItemSize(this.item, "show-content");
|
||||||
|
} else {
|
||||||
|
this.sizer.setItemSize(this.item, itemSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
RoomSizer,
|
||||||
|
RoomDistributor,
|
||||||
|
};
|
100
src/resizer/sizer.js
Normal file
100
src/resizer/sizer.js
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
implements DOM/CSS operations for resizing.
|
||||||
|
The sizer determines what CSS mechanism is used for sizing items, like flexbox, ...
|
||||||
|
*/
|
||||||
|
class Sizer {
|
||||||
|
constructor(container, vertical, reverse) {
|
||||||
|
this.container = container;
|
||||||
|
this.reverse = reverse;
|
||||||
|
this.vertical = vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
getItemPercentage(item) {
|
||||||
|
/*
|
||||||
|
const flexGrow = window.getComputedStyle(item).flexGrow;
|
||||||
|
if (flexGrow === "") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return parseInt(flexGrow) / 1000;
|
||||||
|
*/
|
||||||
|
const style = window.getComputedStyle(item);
|
||||||
|
const sizeStr = this.vertical ? style.height : style.width;
|
||||||
|
const size = parseInt(sizeStr, 10);
|
||||||
|
return size / this.getTotalSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
setItemPercentage(item, percent) {
|
||||||
|
item.style.flexGrow = Math.round(percent * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@param {Element} item the dom element being resized
|
||||||
|
@return {number} how far the edge of the item is from the edge of the container
|
||||||
|
*/
|
||||||
|
getItemOffset(item) {
|
||||||
|
const offset = (this.vertical ? item.offsetTop : item.offsetLeft) - this._getOffset();
|
||||||
|
if (this.reverse) {
|
||||||
|
return this.getTotalSize() - (offset + this.getItemSize(item));
|
||||||
|
} else {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@param {Element} item the dom element being resized
|
||||||
|
@return {number} the width/height of an item in the container
|
||||||
|
*/
|
||||||
|
getItemSize(item) {
|
||||||
|
return this.vertical ? item.offsetHeight : item.offsetWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return {number} the width/height of the container */
|
||||||
|
getTotalSize() {
|
||||||
|
return this.vertical ? this.container.offsetHeight : this.container.offsetWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return {number} container offset to offsetParent */
|
||||||
|
_getOffset() {
|
||||||
|
return this.vertical ? this.container.offsetTop : this.container.offsetLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
setItemSize(item, size) {
|
||||||
|
if (this.vertical) {
|
||||||
|
item.style.height = `${Math.round(size)}px`;
|
||||||
|
} else {
|
||||||
|
item.style.width = `${Math.round(size)}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@param {MouseEvent} event the mouse event
|
||||||
|
@return {number} the distance between the cursor and the edge of the container,
|
||||||
|
along the applicable axis (vertical or horizontal)
|
||||||
|
*/
|
||||||
|
offsetFromEvent(event) {
|
||||||
|
const pos = this.vertical ? event.pageY : event.pageX;
|
||||||
|
if (this.reverse) {
|
||||||
|
return (this._getOffset() + this.getTotalSize()) - pos;
|
||||||
|
} else {
|
||||||
|
return pos - this._getOffset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {Sizer};
|
Loading…
Reference in a new issue