mirror of
https://github.com/element-hq/element-web
synced 2024-11-28 12:28:50 +03:00
Merge pull request #6664 from matrix-org/palid/fix/difficult-to-grab-scrollbar
Fix difficult to grab scrollbar
This commit is contained in:
commit
168fe20559
6 changed files with 67 additions and 32 deletions
|
@ -32,6 +32,10 @@ $roomListCollapsedWidth: 68px;
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
&[data-collapsed] {
|
||||||
|
max-width: $roomListCollapsedWidth;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,12 +44,12 @@ $roomListCollapsedWidth: 68px;
|
||||||
.mx_LeftPanel {
|
.mx_LeftPanel {
|
||||||
background-color: $roomlist-bg-color;
|
background-color: $roomlist-bg-color;
|
||||||
// TODO decrease this once Spaces launches as it'll no longer need to include the 56px Community Panel
|
// TODO decrease this once Spaces launches as it'll no longer need to include the 56px Community Panel
|
||||||
min-width: 206px;
|
|
||||||
|
|
||||||
// Create a row-based flexbox for the GroupFilterPanel and the room list
|
// Create a row-based flexbox for the GroupFilterPanel and the room list
|
||||||
display: flex;
|
display: flex;
|
||||||
contain: content;
|
contain: content;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
// Note: The 'room list' in this context is actually everything that isn't the tag
|
// Note: The 'room list' in this context is actually everything that isn't the tag
|
||||||
// panel, such as the menu options, breadcrumbs, filtering, etc
|
// panel, such as the menu options, breadcrumbs, filtering, etc
|
||||||
|
@ -178,6 +182,8 @@ $roomListCollapsedWidth: 68px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_LeftPanel_roomListWrapper {
|
.mx_LeftPanel_roomListWrapper {
|
||||||
|
// Make the y-scrollbar more responsive
|
||||||
|
padding-right: 2px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-top: 10px; // so we're not up against the search/filter
|
margin-top: 10px; // so we're not up against the search/filter
|
||||||
flex: 1 0 0; // needed in Safari to properly set flex-basis
|
flex: 1 0 0; // needed in Safari to properly set flex-basis
|
||||||
|
@ -199,6 +205,7 @@ $roomListCollapsedWidth: 68px;
|
||||||
|
|
||||||
// These styles override the defaults for the minimized (66px) layout
|
// These styles override the defaults for the minimized (66px) layout
|
||||||
&.mx_LeftPanel_minimized {
|
&.mx_LeftPanel_minimized {
|
||||||
|
flex-grow: 0;
|
||||||
min-width: unset;
|
min-width: unset;
|
||||||
width: unset !important;
|
width: unset !important;
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,8 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
private dispatcherRef: string;
|
private dispatcherRef: string;
|
||||||
protected readonly _matrixClient: MatrixClient;
|
protected readonly _matrixClient: MatrixClient;
|
||||||
protected readonly _roomView: React.RefObject<any>;
|
protected readonly _roomView: React.RefObject<any>;
|
||||||
protected readonly _resizeContainer: React.RefObject<ResizeHandle>;
|
protected readonly _resizeContainer: React.RefObject<HTMLDivElement>;
|
||||||
|
protected readonly resizeHandler: React.RefObject<HTMLDivElement>;
|
||||||
protected compactLayoutWatcherRef: string;
|
protected compactLayoutWatcherRef: string;
|
||||||
protected backgroundImageWatcherRef: string;
|
protected backgroundImageWatcherRef: string;
|
||||||
protected resizer: Resizer;
|
protected resizer: Resizer;
|
||||||
|
@ -176,6 +177,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
this._roomView = React.createRef();
|
this._roomView = React.createRef();
|
||||||
this._resizeContainer = React.createRef();
|
this._resizeContainer = React.createRef();
|
||||||
|
this.resizeHandler = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -280,6 +282,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
isItemCollapsed: domNode => {
|
isItemCollapsed: domNode => {
|
||||||
return domNode.classList.contains("mx_LeftPanel_minimized");
|
return domNode.classList.contains("mx_LeftPanel_minimized");
|
||||||
},
|
},
|
||||||
|
handler: this.resizeHandler.current,
|
||||||
};
|
};
|
||||||
const resizer = new Resizer(this._resizeContainer.current, CollapseDistributor, collapseConfig);
|
const resizer = new Resizer(this._resizeContainer.current, CollapseDistributor, collapseConfig);
|
||||||
resizer.setClassNames({
|
resizer.setClassNames({
|
||||||
|
@ -681,16 +684,17 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
backgroundImage={this.state.backgroundImage}
|
backgroundImage={this.state.backgroundImage}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
ref={this._resizeContainer}
|
|
||||||
className="mx_LeftPanel_wrapper--user"
|
className="mx_LeftPanel_wrapper--user"
|
||||||
|
ref={this._resizeContainer}
|
||||||
|
data-collapsed={this.props.collapseLhs ? true : undefined}
|
||||||
>
|
>
|
||||||
<LeftPanel
|
<LeftPanel
|
||||||
isMinimized={this.props.collapseLhs || false}
|
isMinimized={this.props.collapseLhs || false}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
/>
|
/>
|
||||||
<ResizeHandle id="lp-resizer" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ResizeHandle passRef={this.resizeHandler} id="lp-resizer" />
|
||||||
<div className="mx_RoomView_wrapper">
|
<div className="mx_RoomView_wrapper">
|
||||||
{ pageElement }
|
{ pageElement }
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
|
|
||||||
import React from 'react'; // eslint-disable-line no-unused-vars
|
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
|
//see src/resizer for the actual resizing code, this is just the DOM for the resize handle
|
||||||
const ResizeHandle = (props) => {
|
interface IResizeHandleProps {
|
||||||
|
vertical?: boolean;
|
||||||
|
reverse?: boolean;
|
||||||
|
id?: string;
|
||||||
|
passRef?: React.RefObject<HTMLDivElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResizeHandle: React.FC<IResizeHandleProps> = ({ vertical, reverse, id, passRef }) => {
|
||||||
const classNames = ['mx_ResizeHandle'];
|
const classNames = ['mx_ResizeHandle'];
|
||||||
if (props.vertical) {
|
if (vertical) {
|
||||||
classNames.push('mx_ResizeHandle_vertical');
|
classNames.push('mx_ResizeHandle_vertical');
|
||||||
} else {
|
} else {
|
||||||
classNames.push('mx_ResizeHandle_horizontal');
|
classNames.push('mx_ResizeHandle_horizontal');
|
||||||
}
|
}
|
||||||
if (props.reverse) {
|
if (reverse) {
|
||||||
classNames.push('mx_ResizeHandle_reverse');
|
classNames.push('mx_ResizeHandle_reverse');
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={classNames.join(' ')} data-id={props.id}><div /></div>
|
<div ref={passRef} className={classNames.join(' ')} data-id={id}><div /></div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ResizeHandle.propTypes = {
|
|
||||||
vertical: PropTypes.bool,
|
|
||||||
reverse: PropTypes.bool,
|
|
||||||
id: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ResizeHandle;
|
export default ResizeHandle;
|
|
@ -40,8 +40,13 @@ class CollapseItem extends ResizeItem<ICollapseConfig> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class CollapseDistributor extends FixedDistributor<ICollapseConfig, CollapseItem> {
|
export default class CollapseDistributor extends FixedDistributor<ICollapseConfig, CollapseItem> {
|
||||||
static createItem(resizeHandle: HTMLDivElement, resizer: Resizer<ICollapseConfig>, sizer: Sizer) {
|
static createItem(
|
||||||
return new CollapseItem(resizeHandle, resizer, sizer);
|
resizeHandle: HTMLDivElement,
|
||||||
|
resizer: Resizer<ICollapseConfig>,
|
||||||
|
sizer: Sizer,
|
||||||
|
container?: HTMLElement,
|
||||||
|
): CollapseItem {
|
||||||
|
return new CollapseItem(resizeHandle, resizer, sizer, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly toggleSize: number;
|
private readonly toggleSize: number;
|
||||||
|
@ -55,12 +60,9 @@ export default class CollapseDistributor extends FixedDistributor<ICollapseConfi
|
||||||
|
|
||||||
public resize(newSize: number) {
|
public resize(newSize: number) {
|
||||||
const isCollapsedSize = newSize < this.toggleSize;
|
const isCollapsedSize = newSize < this.toggleSize;
|
||||||
if (isCollapsedSize && !this.isCollapsed) {
|
if (isCollapsedSize !== this.isCollapsed) {
|
||||||
this.isCollapsed = true;
|
this.isCollapsed = isCollapsedSize;
|
||||||
this.item.notifyCollapsed(true);
|
this.item.notifyCollapsed(isCollapsedSize);
|
||||||
} else if (!isCollapsedSize && this.isCollapsed) {
|
|
||||||
this.item.notifyCollapsed(false);
|
|
||||||
this.isCollapsed = false;
|
|
||||||
}
|
}
|
||||||
if (!isCollapsedSize) {
|
if (!isCollapsedSize) {
|
||||||
super.resize(newSize);
|
super.resize(newSize);
|
||||||
|
|
|
@ -26,15 +26,20 @@ export default class ResizeItem<C extends IConfig = IConfig> {
|
||||||
handle: HTMLElement,
|
handle: HTMLElement,
|
||||||
public readonly resizer: Resizer<C>,
|
public readonly resizer: Resizer<C>,
|
||||||
public readonly sizer: Sizer,
|
public readonly sizer: Sizer,
|
||||||
|
public readonly container?: HTMLElement,
|
||||||
) {
|
) {
|
||||||
this.reverse = resizer.isReverseResizeHandle(handle);
|
this.reverse = resizer.isReverseResizeHandle(handle);
|
||||||
this.domNode = <HTMLElement>(this.reverse ? handle.nextElementSibling : handle.previousElementSibling);
|
if (container) {
|
||||||
|
this.domNode = <HTMLElement>(container);
|
||||||
|
} else {
|
||||||
|
this.domNode = <HTMLElement>(this.reverse ? handle.nextElementSibling : handle.previousElementSibling);
|
||||||
|
}
|
||||||
this.id = handle.getAttribute("data-id");
|
this.id = handle.getAttribute("data-id");
|
||||||
}
|
}
|
||||||
|
|
||||||
private copyWith(handle: HTMLElement, resizer: Resizer, sizer: Sizer) {
|
private copyWith(handle: HTMLElement, resizer: Resizer, sizer: Sizer, container?: HTMLElement) {
|
||||||
const Ctor = this.constructor as typeof ResizeItem;
|
const Ctor = this.constructor as typeof ResizeItem;
|
||||||
return new Ctor(handle, resizer, sizer);
|
return new Ctor(handle, resizer, sizer, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
private advance(forwards: boolean) {
|
private advance(forwards: boolean) {
|
||||||
|
|
|
@ -35,6 +35,7 @@ export interface IConfig {
|
||||||
onResizeStart?(): void;
|
onResizeStart?(): void;
|
||||||
onResizeStop?(): void;
|
onResizeStop?(): void;
|
||||||
onResized?(size: number, id: string, element: HTMLElement): void;
|
onResized?(size: number, id: string, element: HTMLElement): void;
|
||||||
|
handler?: HTMLDivElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Resizer<C extends IConfig = IConfig> {
|
export default class Resizer<C extends IConfig = IConfig> {
|
||||||
|
@ -46,8 +47,17 @@ export default class Resizer<C extends IConfig = IConfig> {
|
||||||
public container: HTMLElement,
|
public container: HTMLElement,
|
||||||
private readonly distributorCtor: {
|
private readonly distributorCtor: {
|
||||||
new(item: ResizeItem): FixedDistributor<C, any>;
|
new(item: ResizeItem): FixedDistributor<C, any>;
|
||||||
createItem(resizeHandle: HTMLDivElement, resizer: Resizer, sizer: Sizer): ResizeItem;
|
createItem(
|
||||||
createSizer(containerElement: HTMLElement, vertical: boolean, reverse: boolean): Sizer;
|
resizeHandle: HTMLDivElement,
|
||||||
|
resizer: Resizer,
|
||||||
|
sizer: Sizer,
|
||||||
|
container: HTMLElement
|
||||||
|
): ResizeItem;
|
||||||
|
createSizer(
|
||||||
|
containerElement: HTMLElement,
|
||||||
|
vertical: boolean,
|
||||||
|
reverse: boolean
|
||||||
|
): Sizer;
|
||||||
},
|
},
|
||||||
public readonly config?: C,
|
public readonly config?: C,
|
||||||
) {
|
) {
|
||||||
|
@ -68,12 +78,14 @@ export default class Resizer<C extends IConfig = IConfig> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public attach() {
|
public attach() {
|
||||||
this.container.addEventListener("mousedown", this.onMouseDown, false);
|
const attachment = this?.config?.handler.parentElement ?? this.container;
|
||||||
|
attachment.addEventListener("mousedown", this.onMouseDown, false);
|
||||||
window.addEventListener("resize", this.onResize);
|
window.addEventListener("resize", this.onResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public detach() {
|
public detach() {
|
||||||
this.container.removeEventListener("mousedown", this.onMouseDown, false);
|
const attachment = this?.config?.handler.parentElement ?? this.container;
|
||||||
|
attachment.removeEventListener("mousedown", this.onMouseDown, false);
|
||||||
window.removeEventListener("resize", this.onResize);
|
window.removeEventListener("resize", this.onResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +125,8 @@ export default class Resizer<C extends IConfig = IConfig> {
|
||||||
// use closest in case the resize handle contains
|
// use closest in case the resize handle contains
|
||||||
// child dom nodes that can be the target
|
// child dom nodes that can be the target
|
||||||
const resizeHandle = event.target && (<HTMLDivElement>event.target).closest(`.${this.classNames.handle}`);
|
const resizeHandle = event.target && (<HTMLDivElement>event.target).closest(`.${this.classNames.handle}`);
|
||||||
if (!resizeHandle || resizeHandle.parentElement !== this.container) {
|
const hasHandler = this?.config?.handler;
|
||||||
|
if (!resizeHandle || (!hasHandler && resizeHandle.parentElement !== this.container)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// prevent starting a drag operation
|
// prevent starting a drag operation
|
||||||
|
@ -174,13 +187,17 @@ export default class Resizer<C extends IConfig = IConfig> {
|
||||||
const vertical = resizeHandle.classList.contains(this.classNames.vertical);
|
const vertical = resizeHandle.classList.contains(this.classNames.vertical);
|
||||||
const reverse = this.isReverseResizeHandle(resizeHandle);
|
const reverse = this.isReverseResizeHandle(resizeHandle);
|
||||||
const Distributor = this.distributorCtor;
|
const Distributor = this.distributorCtor;
|
||||||
|
const useItemContainer = this.config && this.config.handler ? this.container : undefined;
|
||||||
const sizer = Distributor.createSizer(this.container, vertical, reverse);
|
const sizer = Distributor.createSizer(this.container, vertical, reverse);
|
||||||
const item = Distributor.createItem(resizeHandle, this, sizer);
|
const item = Distributor.createItem(resizeHandle, this, sizer, useItemContainer);
|
||||||
const distributor = new Distributor(item);
|
const distributor = new Distributor(item);
|
||||||
return { sizer, distributor };
|
return { sizer, distributor };
|
||||||
}
|
}
|
||||||
|
|
||||||
private getResizeHandles() {
|
private getResizeHandles() {
|
||||||
|
if (this?.config?.handler) {
|
||||||
|
return [this.config.handler];
|
||||||
|
}
|
||||||
if (!this.container.children) return [];
|
if (!this.container.children) return [];
|
||||||
return Array.from(this.container.querySelectorAll(`.${this.classNames.handle}`)) as HTMLElement[];
|
return Array.from(this.container.querySelectorAll(`.${this.classNames.handle}`)) as HTMLElement[];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue