Skip to content

Commit

Permalink
- Enchanced util tests;
Browse files Browse the repository at this point in the history
- Reworked a bit util:
  - new implementation of `getInnerHeight` & `getInnerWidth`;
  - new implementation of `shouldReverseRtlScroll` and `getScrollbarWidth`: no need in supplementary dbg methods;
  • Loading branch information
xobotyi committed Aug 30, 2019
1 parent b9e2d3e commit 543bbd4
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 448 deletions.
27 changes: 13 additions & 14 deletions src/Scrollbar.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
import cnb from "cnbuilder";
import * as PropTypes from "prop-types";
import * as React from "react";
import { DraggableData } from "react-draggable";
import { zoomLevel } from "zoom-level";
import Emittr from "./Emittr";
import Loop from "./Loop";
import ScrollbarThumb, { ScrollbarThumbProps } from "./ScrollbarThumb";
import ScrollbarTrack, { ScrollbarTrackClickParameters, ScrollbarTrackProps } from "./ScrollbarTrack";
import defaultStyle from "./style";
import {
AXIS_DIRECTION,
ElementPropsWithElementRefAndRenderer,
ScrollState,
TRACK_CLICK_BEHAVIOR,
TRACK_CLICK_BEHAVIOR_PROP_TYPE
} from "./types";
import * as React from "react";
import * as PropTypes from "prop-types";
import Loop from "./Loop";
import cnb from "cnbuilder";
import ScrollbarTrack, { ScrollbarTrackClickParameters, ScrollbarTrackProps } from "./ScrollbarTrack";
import ScrollbarThumb, { ScrollbarThumbProps } from "./ScrollbarThumb";
import * as util from "./util";
import { DraggableData } from "react-draggable";
import Emittr from "./Emittr";
import defaultStyle from "./style";
import { zoomLevel } from "zoom-level";
import { renderDivWithRenderer } from "./util";

declare var global: {
window?: Window;
};

const reverseRTL: boolean = util.shouldReverseRTLScroll();
let pageZoomLevel: number = global.window ? zoomLevel() : 1;
global.window && global.window.addEventListener("resize", () => (pageZoomLevel = zoomLevel()), { passive: true });

Expand Down Expand Up @@ -1054,7 +1053,7 @@ export default class Scrollbar extends React.Component<ScrollbarProps, Scrollbar
scrollValues.scrollLeft
);

if (this.state.isRTL && reverseRTL) {
if (this.state.isRTL && util.shouldReverseRtlScroll()) {
thumbOffset += trackInnerSize - thumbSize;
}

Expand Down Expand Up @@ -1130,7 +1129,7 @@ export default class Scrollbar extends React.Component<ScrollbarProps, Scrollbar
const thumbSize = this.thumbXElement.clientWidth;
const trackInnerSize = util.getInnerWidth(this.trackXElement);
const thumbOffset =
(this.scrollValues.isRTL && reverseRTL
(this.scrollValues.isRTL && util.shouldReverseRtlScroll()
? values.offset + thumbSize / 2 - trackInnerSize
: values.offset - thumbSize / 2) -
//@ts-ignore
Expand Down Expand Up @@ -1249,7 +1248,7 @@ export default class Scrollbar extends React.Component<ScrollbarProps, Scrollbar
const trackInnerSize = trackRect.width - paddingLeft - paddingRight;
const thumbSize = this.thumbXElement.clientWidth;
const offset =
this.scrollValues.isRTL && reverseRTL
this.scrollValues.isRTL && util.shouldReverseRtlScroll()
? data.x + thumbSize - trackInnerSize + paddingLeft
: data.lastX - paddingLeft;

Expand Down
9 changes: 4 additions & 5 deletions src/ScrollbarThumb.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as React from "react";
import * as PropTypes from "prop-types";
import cnb from "cnbuilder";
import * as PropTypes from "prop-types";
import * as React from "react";
import { DraggableCore, DraggableData, DraggableEvent } from "react-draggable";
import { AXIS_DIRECTION, AXIS_DIRECTION_PROP_TYPE, ElementPropsWithElementRefAndRenderer } from "./types";
import { renderDivWithRenderer, isFun, isUndef } from "./util";
import { isFun, isUndef, renderDivWithRenderer } from "./util";

declare var global: {
document?: Document;
Expand Down Expand Up @@ -43,7 +43,7 @@ export default class ScrollbarThumb extends React.Component<ScrollbarThumbProps,
lastY: 0
};
public element: HTMLDivElement | null = null;
private prevUserSelect: string | null;
private prevUserSelect: string;
private prevOnSelectStart: ((ev: Event) => boolean) | null;

private static selectStartReplacer = () => false;
Expand Down Expand Up @@ -131,7 +131,6 @@ export default class ScrollbarThumb extends React.Component<ScrollbarThumbProps,

if (global.document) {
global.document.body.style.userSelect = this.prevUserSelect;
this.prevUserSelect = null;

global.document.onselectstart = this.prevOnSelectStart;
this.prevOnSelectStart = null;
Expand Down
4 changes: 2 additions & 2 deletions src/ScrollbarTrack.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from "react";
import * as PropTypes from "prop-types";
import cnb from "cnbuilder";
import * as PropTypes from "prop-types";
import * as React from "react";
import { AXIS_DIRECTION, AXIS_DIRECTION_PROP_TYPE, ElementPropsWithElementRefAndRenderer } from "./types";
import { isFun, isUndef, renderDivWithRenderer } from "./util";

Expand Down
3 changes: 1 addition & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as React from "react";
import * as PropTypes from "prop-types";
import { isFun } from "./util";
import * as React from "react";

export enum AXIS_DIRECTION {
X = "x",
Expand Down
179 changes: 62 additions & 117 deletions src/util.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,78 +38,34 @@ export function renderDivWithRenderer(props: ElementPropsWithElementRefAndRender
return <div {...props} ref={elementRef} />;
}

/**
* @description Return element's height without padding
*
* ts-ignore here is okay here, because it brigs around 40% of performance
*/
export const getInnerHeight = (el: HTMLDivElement): number => {
function getInnerSize(el: HTMLElement, dimension: string, padding1: string, padding2: string): number {
const styles = getComputedStyle(el);

if (styles.boxSizing === "border-box") {
return Math.max(
(parseFloat(styles.height as string) || 0) -
(parseFloat(styles.paddingTop as string) || 0) -
(parseFloat(styles.paddingBottom as string) || 0),
0
0,
(parseFloat(styles[dimension] as string) || 0) -
(parseFloat(styles[padding1] as string) || 0) -
(parseFloat(styles[padding2] as string) || 0)
);
}

// @ts-ignore
return parseFloat(styles.height) || 0;
};
return parseFloat(styles[dimension] as string) || 0;
}

/**
* @description Return element's width without padding
*
* ts-ignore here is okay here, because it brigs around 40% of performance
* @description Return element's height without padding
*/
export const getInnerWidth = (el: HTMLDivElement): number => {
const styles = getComputedStyle(el);

if (styles.boxSizing === "border-box") {
return Math.max(
(parseFloat(styles.width as string) || 0) -
(parseFloat(styles.paddingLeft as string) || 0) -
(parseFloat(styles.paddingRight as string) || 0),
0
);
}

// @ts-ignore
return parseFloat(styles.width) || 0;
};
export function getInnerHeight(el: HTMLElement): number {
return getInnerSize(el, "height", "paddingTop", "paddingBottom");
}

/**
* @description Return element's dimensions without padding
*
* ts-ignore here is okay here, because it brigs around 40% of performance
* @description Return element's width without padding
*/
export const getInnerDimensions = (el: HTMLDivElement): { width: number; height: number } => {
let styles = getComputedStyle(el);

if (styles.boxSizing === "border-box") {
return {
height: Math.max(
(parseFloat(styles.height as string) || 0) -
(parseFloat(styles.paddingTop as string) || 0) -
(parseFloat(styles.paddingBottom as string) || 0),
0
),
width: Math.max(
// @ts-ignore
(parseFloat(styles.width as string) || 0) -
(parseFloat(styles.paddingLeft as string) || 0) -
(parseFloat(styles.paddingRight as string) || 0),
0
)
};
}
return {
height: parseFloat(styles.height as string) || 0,
width: parseFloat(styles.width as string) || 0
};
};
export function getInnerWidth(el: HTMLElement): number {
return getInnerSize(el, "width", "paddingLeft", "paddingRight");
}

/**
* @description Return unique UUID v4
Expand Down Expand Up @@ -206,42 +162,6 @@ export function calcScrollForThumbOffset(
return (thumbOffset * (contentSize - viewportSize)) / (trackSize - thumbSize);
}

let scrollbarWidth: number | null = null;

/**
* @description Returns scrollbar width specific for current environment
*/
export function getScrollbarWidth(force = false) {
if (!force && scrollbarWidth !== null) {
return scrollbarWidth;
}

if (!doc) {
return (scrollbarWidth = 0);
}

let el = doc.createElement("div");
el.setAttribute("style", "display:block;position:absolute;width:100px;height:100px;top:-9999px;overflow:scroll;");

doc.body.appendChild(el);
scrollbarWidth = el.offsetWidth - el.clientWidth;
doc.body.removeChild(el);

return scrollbarWidth;
}

/**
* @description Set the cached width to given value.<br/>
* <i>null</i> will force to recalculate value on next get.
*/
export const _dbgSetScrollbarWidth = (v: number | null): number | null => {
if (v === null || isNum(v)) {
return (scrollbarWidth = v);
}

throw new TypeError("override value expected to be a number or null, got " + typeof v);
};

/**
* @description Set the document node to calculate the scrollbar width.<br/>
* <i>null</i> will force getter to return 0 (it'll imitate SSR).
Expand All @@ -259,42 +179,67 @@ export const _dbgSetDocument = (v: Document | null): Document | null => {
*/
export const _dbgGetDocument = (): Document | null => doc;

let isReverseRTLScrollNeeded: boolean | null = null;
/**
* @description Returns scrollbar width specific for current environment
*/
export function getScrollbarWidth(force: boolean = false) {
if (!force && !isUndef(getScrollbarWidth._cache)) {
return getScrollbarWidth._cache;
}

if (!doc) {
return (getScrollbarWidth._cache = 0);
}

let el = doc.createElement("div");
el.setAttribute("style", "position:absolute;width:100px;height:100px;top:-999px;left:-999px;overflow:scroll;");

doc.body.appendChild(el);

getScrollbarWidth._cache = 100 - el.clientWidth;

doc.body.removeChild(el);

return getScrollbarWidth._cache;
}

export namespace getScrollbarWidth {
export let _cache: number;
}

/**
* @description Detect need of horizontal scroll reverse while RTL
*/
export const shouldReverseRTLScroll = (force: boolean = false): boolean => {
if (!force && isReverseRTLScrollNeeded !== null) {
return isReverseRTLScrollNeeded;
export function shouldReverseRtlScroll(force: boolean = false): boolean {
if (!force && !isUndef(shouldReverseRtlScroll._cache)) {
return shouldReverseRtlScroll._cache;
}

if (!doc) {
return (isReverseRTLScrollNeeded = false);
return (shouldReverseRtlScroll._cache = false);
}

let el = doc.createElement("div");
let child = doc.createElement("div");
const el = doc.createElement("div");
const child = doc.createElement("div");

el.appendChild(child);

el.setAttribute(
"style",
"display:block;position:absolute;width:100px;height:100px;top:-9999px;overflow:scroll;direction:rtl;"
"position:absolute;width:100px;height:100px;top:-999px;left:-999px;overflow:scroll;direction:rtl"
);
child.setAttribute("style", "display:block;position:relative;width:1000px;height:1000px;direction:rtl;");
el.appendChild(child);
child.setAttribute("style", "width:1000px;height:1000px");

doc.body.appendChild(el);
el.scrollLeft;
el.scrollLeft = 45;
isReverseRTLScrollNeeded = el.scrollLeft === 0;

el.scrollLeft = -50;
shouldReverseRtlScroll._cache = el.scrollLeft === -50;

doc.body.removeChild(el);

return isReverseRTLScrollNeeded;
};
return shouldReverseRtlScroll._cache;
}

/**
* @description Set the cached value to given value.<br/>
* <i>null</i> will force to recalculate value on next get.
*/
export const _dbgSetIsReverseRTLScrollNeeded = (v: boolean | null): boolean | null => {
return (isReverseRTLScrollNeeded = v === null ? null : Boolean(v));
};
export namespace shouldReverseRtlScroll {
export let _cache: boolean;
}
9 changes: 1 addition & 8 deletions testbench/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,7 @@ export function renderAmountOfParagraphs(amount = 5, paragraphsProps: React.HTML
}

ReactDom.render(
<Scrollbar
style={{ width: 528, height: 200 }}
scrollerProps={{
elementRef: ref => {
console.log(ref);
}
}}
>
<Scrollbar style={{ width: 528, height: 200 }}>
{renderAmountOfParagraphs(10, { style: { width: "150%" } })}
</Scrollbar>,
document.getElementById("AppRoot")
Expand Down
Loading

0 comments on commit 543bbd4

Please sign in to comment.