mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-22 10:30:38 +03:00
size observer
This commit is contained in:
@@ -14,6 +14,8 @@ module.exports = function (api) {
|
|||||||
[
|
[
|
||||||
'@babel/preset-env',
|
'@babel/preset-env',
|
||||||
{
|
{
|
||||||
|
useBuiltIns: 'usage',
|
||||||
|
corejs: { version: 3, proposals: true },
|
||||||
targets: {
|
targets: {
|
||||||
node: 'current',
|
node: 'current',
|
||||||
},
|
},
|
||||||
|
|||||||
+119
-20
@@ -12,6 +12,45 @@ const rollupNodeEnv = 'build';
|
|||||||
const cacheFilePrefix = 'jest-puppeteer-overlayscrollbars-cache-';
|
const cacheFilePrefix = 'jest-puppeteer-overlayscrollbars-cache-';
|
||||||
const cacheEncoding = 'utf8';
|
const cacheEncoding = 'utf8';
|
||||||
const cacheHash = 'md5';
|
const cacheHash = 'md5';
|
||||||
|
const legacyBabelConfigAssign = {
|
||||||
|
exclude: [/\/core-js\//],
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
'@babel/preset-env',
|
||||||
|
{
|
||||||
|
useBuiltIns: 'usage',
|
||||||
|
corejs: { version: 3, proposals: true },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeBabelConfigs = (currentConfig, mergeConfig) => {
|
||||||
|
const { presets: assignPresets, exclude: assignExclude } = mergeConfig;
|
||||||
|
const { presets: configPresets, exclude: configExclude } = currentConfig;
|
||||||
|
|
||||||
|
assignPresets.forEach((assignPreset) => {
|
||||||
|
if (Array.isArray(assignPreset)) {
|
||||||
|
const [assignName, assignConfig] = assignPreset;
|
||||||
|
|
||||||
|
configPresets.forEach((configPreset) => {
|
||||||
|
if (Array.isArray(configPreset)) {
|
||||||
|
const [configName, configConfig] = configPreset;
|
||||||
|
|
||||||
|
if (configName === assignName && typeof configConfig === 'object' && typeof assignConfig === 'object') {
|
||||||
|
Object.assign(configConfig, {
|
||||||
|
...assignConfig,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const finalAssignExclude = Array.isArray(assignExclude) ? assignExclude : [assignExclude];
|
||||||
|
const finalConfigExclude = Array.isArray(configExclude) ? configExclude : [configExclude, ...finalAssignExclude];
|
||||||
|
currentConfig.exclude = finalConfigExclude.filter((exc) => !!exc);
|
||||||
|
};
|
||||||
|
|
||||||
const makeHtmlAttributes = (attributes) => {
|
const makeHtmlAttributes = (attributes) => {
|
||||||
if (!attributes) {
|
if (!attributes) {
|
||||||
@@ -40,11 +79,68 @@ const genHtmlTemplateFunc = (content) => ({ attributes, files, meta, publicPath,
|
|||||||
<head>
|
<head>
|
||||||
${metas}
|
${metas}
|
||||||
<title>${title}</title>
|
<title>${title}</title>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
#testResult {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 5px;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
#testResult.passed {
|
||||||
|
display: block;
|
||||||
|
background: lime;
|
||||||
|
}
|
||||||
|
#testResult.passed::before {
|
||||||
|
content: 'success';
|
||||||
|
}
|
||||||
|
#testResult.failed {
|
||||||
|
display: block;
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
#testResult.failed::before {
|
||||||
|
content: 'failed';
|
||||||
|
}
|
||||||
|
</style>
|
||||||
${links}
|
${links}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
${content || ''}
|
${content || ''}
|
||||||
${scripts}
|
${scripts}
|
||||||
|
<div id="testResult"></div>
|
||||||
|
<script>
|
||||||
|
var testResultElm = document.getElementById('testResult');
|
||||||
|
window.setTestResult = function(result) {
|
||||||
|
if (typeof result === 'boolean') {
|
||||||
|
testResultElm.setAttribute('class', result ? 'passed' : 'failed');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
testResultElm.removeAttribute('class');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.testPassed = function() {
|
||||||
|
return testResultElm.getAttribute('class') === 'passed';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
};
|
};
|
||||||
@@ -112,26 +208,29 @@ const setupRollupTest = async (rootDir, testPath, cacheDir) => {
|
|||||||
|
|
||||||
let rollupConfigObj = rollupConfig(undefined, {
|
let rollupConfigObj = rollupConfig(undefined, {
|
||||||
project: rootDir,
|
project: rootDir,
|
||||||
overwrite: (rollupConfigDefaults) => ({
|
overwrite: (rollupConfigDefaults, legacyBabelConfig) => {
|
||||||
input: path.resolve(testDir, deploymentConfig.js.input),
|
mergeBabelConfigs(legacyBabelConfig, legacyBabelConfigAssign);
|
||||||
dist: path.resolve(testDir, deploymentConfig.build),
|
return {
|
||||||
file: deploymentConfig.js.output,
|
input: path.resolve(testDir, deploymentConfig.js.input),
|
||||||
types: null,
|
dist: path.resolve(testDir, deploymentConfig.build),
|
||||||
minVersions: false,
|
file: deploymentConfig.js.output,
|
||||||
esmBuild: false,
|
types: null,
|
||||||
sourcemap: true,
|
minVersions: false,
|
||||||
name: testName,
|
esmBuild: false,
|
||||||
pipeline: [
|
sourcemap: true,
|
||||||
rollupPluginStyles(),
|
name: testName,
|
||||||
...rollupConfigDefaults.pipeline,
|
pipeline: [
|
||||||
rollupPluginHtml({
|
rollupPluginStyles(),
|
||||||
title: `Jest-Puppeteer: ${testName}`,
|
...rollupConfigDefaults.pipeline,
|
||||||
fileName: deploymentConfig.html.output,
|
rollupPluginHtml({
|
||||||
template: genHtmlTemplateFunc(htmlFileContent),
|
title: `Jest-Puppeteer: ${testName}`,
|
||||||
meta: [{ charset: 'utf-8' }, { 'http-equiv': 'X-UA-Compatible', content: 'IE=edge' }],
|
fileName: deploymentConfig.html.output,
|
||||||
}),
|
template: genHtmlTemplateFunc(htmlFileContent),
|
||||||
],
|
meta: [{ charset: 'utf-8' }, { 'http-equiv': 'X-UA-Compatible', content: 'IE=edge' }],
|
||||||
}),
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
silent: true,
|
silent: true,
|
||||||
fast: true,
|
fast: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ process.env.TEST_SERVER_PORT = port;
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
browser: 'chromium',
|
browser: 'chromium',
|
||||||
browserContext: 'incognito',
|
browserContext: 'incognito',
|
||||||
|
launch: {
|
||||||
|
headless: false,
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
command: `cross-env TEST_SERVER_PORT=${port} node ${testServerPath}`,
|
command: `cross-env TEST_SERVER_PORT=${port} node ${testServerPath}`,
|
||||||
port,
|
port,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"babel-jest": "^26.0.1",
|
"babel-jest": "^26.0.1",
|
||||||
"bufferutil": "^4.0.1",
|
"bufferutil": "^4.0.1",
|
||||||
"canvas": "^2.6.1",
|
"canvas": "^2.6.1",
|
||||||
|
"core-js": "^3.6.5",
|
||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"del": "^5.1.0",
|
"del": "^5.1.0",
|
||||||
"eslint": "^7.5.0",
|
"eslint": "^7.5.0",
|
||||||
|
|||||||
+12
-10
@@ -335,6 +335,7 @@ const optionsTemplateTypes = ['boolean', 'number', 'string', 'array', 'object',
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const { abs, round } = Math;
|
const { abs, round } = Math;
|
||||||
|
const envornmentElmId = 'os-envornment';
|
||||||
|
|
||||||
const nativeScrollbarSize = (body, measureElm) => {
|
const nativeScrollbarSize = (body, measureElm) => {
|
||||||
appendChildren(body, measureElm);
|
appendChildren(body, measureElm);
|
||||||
@@ -363,6 +364,7 @@ const rtlScrollBehavior = (parentElm, childElm) => {
|
|||||||
style(parentElm, {
|
style(parentElm, {
|
||||||
overflowX: strHidden,
|
overflowX: strHidden,
|
||||||
overflowY: strHidden,
|
overflowY: strHidden,
|
||||||
|
direction: 'rtl',
|
||||||
});
|
});
|
||||||
scrollLeft(parentElm, 0);
|
scrollLeft(parentElm, 0);
|
||||||
const parentOffset = absoluteCoordinates(parentElm);
|
const parentOffset = absoluteCoordinates(parentElm);
|
||||||
@@ -419,7 +421,7 @@ class Environment {
|
|||||||
const _self = this;
|
const _self = this;
|
||||||
|
|
||||||
const { body } = document;
|
const { body } = document;
|
||||||
const envDOM = createDOM('<div id="os-dummy-scrollbar-size"><div></div></div>');
|
const envDOM = createDOM(`<div id="${envornmentElmId}"><div></div></div>`);
|
||||||
const envElm = envDOM[0];
|
const envElm = envDOM[0];
|
||||||
const envChildElm = envElm.firstChild;
|
const envChildElm = envElm.firstChild;
|
||||||
const nScrollBarSize = nativeScrollbarSize(body, envElm);
|
const nScrollBarSize = nativeScrollbarSize(body, envElm);
|
||||||
@@ -427,13 +429,13 @@ class Environment {
|
|||||||
x: nScrollBarSize.x === 0,
|
x: nScrollBarSize.x === 0,
|
||||||
y: nScrollBarSize.y === 0,
|
y: nScrollBarSize.y === 0,
|
||||||
};
|
};
|
||||||
_self.autoUpdateLoop = false;
|
_self._autoUpdateLoop = false;
|
||||||
_self.nativeScrollbarSize = nScrollBarSize;
|
_self._nativeScrollbarSize = nScrollBarSize;
|
||||||
_self.nativeScrollbarIsOverlaid = nativeScrollbarIsOverlaid;
|
_self._nativeScrollbarIsOverlaid = nativeScrollbarIsOverlaid;
|
||||||
_self.nativeScrollbarStyling = nativeScrollbarStyling(envElm);
|
_self._nativeScrollbarStyling = nativeScrollbarStyling(envElm);
|
||||||
_self.rtlScrollBehavior = rtlScrollBehavior(envElm, envChildElm);
|
_self._rtlScrollBehavior = rtlScrollBehavior(envElm, envChildElm);
|
||||||
_self.supportPassiveEvents = passiveEvents();
|
_self._supportPassiveEvents = passiveEvents();
|
||||||
_self.supportResizeObserver = !!jsAPI('ResizeObserver');
|
_self._supportResizeObserver = !!jsAPI('ResizeObserver');
|
||||||
removeAttr(envElm, 'style');
|
removeAttr(envElm, 'style');
|
||||||
removeElements(envElm);
|
removeElements(envElm);
|
||||||
|
|
||||||
@@ -464,11 +466,11 @@ class Environment {
|
|||||||
const difference = !diffBiggerThanOne(deltaAbsRatio.w, deltaAbsRatio.h);
|
const difference = !diffBiggerThanOne(deltaAbsRatio.w, deltaAbsRatio.h);
|
||||||
const dprChanged = dprNew !== dpr && dpr > 0;
|
const dprChanged = dprNew !== dpr && dpr > 0;
|
||||||
const isZoom = deltaIsBigger && difference && dprChanged;
|
const isZoom = deltaIsBigger && difference && dprChanged;
|
||||||
const oldScrollbarSize = _self.nativeScrollbarSize;
|
const oldScrollbarSize = _self._nativeScrollbarSize;
|
||||||
let newScrollbarSize;
|
let newScrollbarSize;
|
||||||
|
|
||||||
if (isZoom) {
|
if (isZoom) {
|
||||||
newScrollbarSize = _self.nativeScrollbarSize = nativeScrollbarSize(body, envElm);
|
newScrollbarSize = _self._nativeScrollbarSize = nativeScrollbarSize(body, envElm);
|
||||||
removeElements(envElm);
|
removeElements(envElm);
|
||||||
|
|
||||||
if (oldScrollbarSize.x !== newScrollbarSize.x || oldScrollbarSize.y !== newScrollbarSize.y) {
|
if (oldScrollbarSize.x !== newScrollbarSize.x || oldScrollbarSize.y !== newScrollbarSize.y) {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+12
-10
@@ -377,6 +377,7 @@
|
|||||||
|
|
||||||
var abs = Math.abs,
|
var abs = Math.abs,
|
||||||
round = Math.round;
|
round = Math.round;
|
||||||
|
var envornmentElmId = 'os-envornment';
|
||||||
|
|
||||||
var nativeScrollbarSize = function nativeScrollbarSize(body, measureElm) {
|
var nativeScrollbarSize = function nativeScrollbarSize(body, measureElm) {
|
||||||
appendChildren(body, measureElm);
|
appendChildren(body, measureElm);
|
||||||
@@ -406,6 +407,7 @@
|
|||||||
style(parentElm, {
|
style(parentElm, {
|
||||||
overflowX: strHidden,
|
overflowX: strHidden,
|
||||||
overflowY: strHidden,
|
overflowY: strHidden,
|
||||||
|
direction: 'rtl',
|
||||||
});
|
});
|
||||||
scrollLeft(parentElm, 0);
|
scrollLeft(parentElm, 0);
|
||||||
var parentOffset = absoluteCoordinates(parentElm);
|
var parentOffset = absoluteCoordinates(parentElm);
|
||||||
@@ -463,7 +465,7 @@
|
|||||||
|
|
||||||
var _document = document,
|
var _document = document,
|
||||||
body = _document.body;
|
body = _document.body;
|
||||||
var envDOM = createDOM('<div id="os-dummy-scrollbar-size"><div></div></div>');
|
var envDOM = createDOM('<div id="' + envornmentElmId + '"><div></div></div>');
|
||||||
var envElm = envDOM[0];
|
var envElm = envDOM[0];
|
||||||
var envChildElm = envElm.firstChild;
|
var envChildElm = envElm.firstChild;
|
||||||
var nScrollBarSize = nativeScrollbarSize(body, envElm);
|
var nScrollBarSize = nativeScrollbarSize(body, envElm);
|
||||||
@@ -471,13 +473,13 @@
|
|||||||
x: nScrollBarSize.x === 0,
|
x: nScrollBarSize.x === 0,
|
||||||
y: nScrollBarSize.y === 0,
|
y: nScrollBarSize.y === 0,
|
||||||
};
|
};
|
||||||
_self.autoUpdateLoop = false;
|
_self._autoUpdateLoop = false;
|
||||||
_self.nativeScrollbarSize = nScrollBarSize;
|
_self._nativeScrollbarSize = nScrollBarSize;
|
||||||
_self.nativeScrollbarIsOverlaid = nativeScrollbarIsOverlaid;
|
_self._nativeScrollbarIsOverlaid = nativeScrollbarIsOverlaid;
|
||||||
_self.nativeScrollbarStyling = nativeScrollbarStyling(envElm);
|
_self._nativeScrollbarStyling = nativeScrollbarStyling(envElm);
|
||||||
_self.rtlScrollBehavior = rtlScrollBehavior(envElm, envChildElm);
|
_self._rtlScrollBehavior = rtlScrollBehavior(envElm, envChildElm);
|
||||||
_self.supportPassiveEvents = passiveEvents();
|
_self._supportPassiveEvents = passiveEvents();
|
||||||
_self.supportResizeObserver = !!jsAPI('ResizeObserver');
|
_self._supportResizeObserver = !!jsAPI('ResizeObserver');
|
||||||
removeAttr(envElm, 'style');
|
removeAttr(envElm, 'style');
|
||||||
removeElements(envElm);
|
removeElements(envElm);
|
||||||
|
|
||||||
@@ -508,11 +510,11 @@
|
|||||||
var difference = !diffBiggerThanOne(deltaAbsRatio.w, deltaAbsRatio.h);
|
var difference = !diffBiggerThanOne(deltaAbsRatio.w, deltaAbsRatio.h);
|
||||||
var dprChanged = dprNew !== dpr && dpr > 0;
|
var dprChanged = dprNew !== dpr && dpr > 0;
|
||||||
var isZoom = deltaIsBigger && difference && dprChanged;
|
var isZoom = deltaIsBigger && difference && dprChanged;
|
||||||
var oldScrollbarSize = _self.nativeScrollbarSize;
|
var oldScrollbarSize = _self._nativeScrollbarSize;
|
||||||
var newScrollbarSize;
|
var newScrollbarSize;
|
||||||
|
|
||||||
if (isZoom) {
|
if (isZoom) {
|
||||||
newScrollbarSize = _self.nativeScrollbarSize = nativeScrollbarSize(body, envElm);
|
newScrollbarSize = _self._nativeScrollbarSize = nativeScrollbarSize(body, envElm);
|
||||||
removeElements(envElm);
|
removeElements(envElm);
|
||||||
|
|
||||||
if (oldScrollbarSize.x !== newScrollbarSize.x || oldScrollbarSize.y !== newScrollbarSize.y) {
|
if (oldScrollbarSize.x !== newScrollbarSize.x || oldScrollbarSize.y !== newScrollbarSize.y) {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -17,6 +17,7 @@ import {
|
|||||||
type OnEnvironmentChanged = (env: Environment) => void;
|
type OnEnvironmentChanged = (env: Environment) => void;
|
||||||
|
|
||||||
const { abs, round } = Math;
|
const { abs, round } = Math;
|
||||||
|
const envornmentElmId = 'os-envornment';
|
||||||
|
|
||||||
const nativeScrollbarSize = (body: HTMLElement, measureElm: HTMLElement): XY => {
|
const nativeScrollbarSize = (body: HTMLElement, measureElm: HTMLElement): XY => {
|
||||||
appendChildren(body, measureElm);
|
appendChildren(body, measureElm);
|
||||||
@@ -42,7 +43,7 @@ const nativeScrollbarStyling = (testElm: HTMLElement): boolean => {
|
|||||||
|
|
||||||
const rtlScrollBehavior = (parentElm: HTMLElement, childElm: HTMLElement): { i: boolean; n: boolean } => {
|
const rtlScrollBehavior = (parentElm: HTMLElement, childElm: HTMLElement): { i: boolean; n: boolean } => {
|
||||||
const strHidden = 'hidden';
|
const strHidden = 'hidden';
|
||||||
style(parentElm, { overflowX: strHidden, overflowY: strHidden });
|
style(parentElm, { overflowX: strHidden, overflowY: strHidden, direction: 'rtl' });
|
||||||
scrollLeft(parentElm, 0);
|
scrollLeft(parentElm, 0);
|
||||||
|
|
||||||
const parentOffset = absoluteCoordinates(parentElm);
|
const parentOffset = absoluteCoordinates(parentElm);
|
||||||
@@ -105,24 +106,24 @@ const diffBiggerThanOne = (valOne: number, valTwo: number): boolean => {
|
|||||||
export class Environment {
|
export class Environment {
|
||||||
#onChangedListener: Set<OnEnvironmentChanged> = new Set();
|
#onChangedListener: Set<OnEnvironmentChanged> = new Set();
|
||||||
|
|
||||||
autoUpdateLoop!: boolean;
|
_autoUpdateLoop!: boolean;
|
||||||
|
|
||||||
nativeScrollbarSize!: XY;
|
_nativeScrollbarSize!: XY;
|
||||||
|
|
||||||
nativeScrollbarIsOverlaid!: XY<boolean>;
|
_nativeScrollbarIsOverlaid!: XY<boolean>;
|
||||||
|
|
||||||
nativeScrollbarStyling!: boolean;
|
_nativeScrollbarStyling!: boolean;
|
||||||
|
|
||||||
rtlScrollBehavior!: { n: boolean; i: boolean };
|
_rtlScrollBehavior!: { n: boolean; i: boolean };
|
||||||
|
|
||||||
supportPassiveEvents!: boolean;
|
_supportPassiveEvents!: boolean;
|
||||||
|
|
||||||
supportResizeObserver!: boolean;
|
_supportResizeObserver!: boolean;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const _self = this;
|
const _self = this;
|
||||||
const { body } = document;
|
const { body } = document;
|
||||||
const envDOM = createDOM('<div id="os-dummy-scrollbar-size"><div></div></div>');
|
const envDOM = createDOM(`<div id="${envornmentElmId}"><div></div></div>`);
|
||||||
const envElm = envDOM[0] as HTMLElement;
|
const envElm = envDOM[0] as HTMLElement;
|
||||||
const envChildElm = envElm.firstChild as HTMLElement;
|
const envChildElm = envElm.firstChild as HTMLElement;
|
||||||
|
|
||||||
@@ -132,13 +133,13 @@ export class Environment {
|
|||||||
y: nScrollBarSize.y === 0,
|
y: nScrollBarSize.y === 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
_self.autoUpdateLoop = false;
|
_self._autoUpdateLoop = false;
|
||||||
_self.nativeScrollbarSize = nScrollBarSize;
|
_self._nativeScrollbarSize = nScrollBarSize;
|
||||||
_self.nativeScrollbarIsOverlaid = nativeScrollbarIsOverlaid;
|
_self._nativeScrollbarIsOverlaid = nativeScrollbarIsOverlaid;
|
||||||
_self.nativeScrollbarStyling = nativeScrollbarStyling(envElm);
|
_self._nativeScrollbarStyling = nativeScrollbarStyling(envElm);
|
||||||
_self.rtlScrollBehavior = rtlScrollBehavior(envElm, envChildElm);
|
_self._rtlScrollBehavior = rtlScrollBehavior(envElm, envChildElm);
|
||||||
_self.supportPassiveEvents = passiveEvents();
|
_self._supportPassiveEvents = passiveEvents();
|
||||||
_self.supportResizeObserver = !!jsAPI('ResizeObserver');
|
_self._supportResizeObserver = !!jsAPI('ResizeObserver');
|
||||||
|
|
||||||
removeAttr(envElm, 'style');
|
removeAttr(envElm, 'style');
|
||||||
removeElements(envElm);
|
removeElements(envElm);
|
||||||
@@ -172,11 +173,11 @@ export class Environment {
|
|||||||
const dprChanged = dprNew !== dpr && dpr > 0;
|
const dprChanged = dprNew !== dpr && dpr > 0;
|
||||||
const isZoom = deltaIsBigger && difference && dprChanged;
|
const isZoom = deltaIsBigger && difference && dprChanged;
|
||||||
|
|
||||||
const oldScrollbarSize = _self.nativeScrollbarSize;
|
const oldScrollbarSize = _self._nativeScrollbarSize;
|
||||||
let newScrollbarSize;
|
let newScrollbarSize;
|
||||||
|
|
||||||
if (isZoom) {
|
if (isZoom) {
|
||||||
newScrollbarSize = _self.nativeScrollbarSize = nativeScrollbarSize(body, envElm);
|
newScrollbarSize = _self._nativeScrollbarSize = nativeScrollbarSize(body, envElm);
|
||||||
removeElements(envElm);
|
removeElements(envElm);
|
||||||
|
|
||||||
if (oldScrollbarSize.x !== newScrollbarSize.x || oldScrollbarSize.y !== newScrollbarSize.y) {
|
if (oldScrollbarSize.x !== newScrollbarSize.x || oldScrollbarSize.y !== newScrollbarSize.y) {
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
@import './sizeobserver.scss';
|
||||||
|
|
||||||
|
#os-envornment {
|
||||||
|
position: fixed;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
overflow: scroll;
|
||||||
|
height: 500px;
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
#os-envornment > div {
|
||||||
|
width: 200%;
|
||||||
|
height: 200%;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
/* fix restricted measuring */
|
||||||
|
#os-envornment:before,
|
||||||
|
#os-envornment:after,
|
||||||
|
.os-content:before,
|
||||||
|
.os-content:after {
|
||||||
|
content: '';
|
||||||
|
display: table;
|
||||||
|
width: 0.01px;
|
||||||
|
height: 0.01px;
|
||||||
|
line-height: 0;
|
||||||
|
font-size: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
#os-envornment,
|
||||||
|
.os-viewport {
|
||||||
|
-ms-overflow-style: scrollbar !important;
|
||||||
|
}
|
||||||
|
.os-viewport-native-scrollbars-invisible#os-envornment,
|
||||||
|
.os-viewport-native-scrollbars-invisible.os-viewport {
|
||||||
|
scrollbar-width: none !important;
|
||||||
|
}
|
||||||
|
.os-viewport-native-scrollbars-invisible#os-envornment::-webkit-scrollbar,
|
||||||
|
.os-viewport-native-scrollbars-invisible.os-viewport::-webkit-scrollbar,
|
||||||
|
.os-viewport-native-scrollbars-invisible#os-envornment::-webkit-scrollbar-corner,
|
||||||
|
.os-viewport-native-scrollbars-invisible.os-viewport::-webkit-scrollbar-corner {
|
||||||
|
display: none !important;
|
||||||
|
width: 0px !important;
|
||||||
|
height: 0px !important;
|
||||||
|
visibility: hidden !important;
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { validate, assignDeep } from 'support';
|
||||||
|
import { Options, optionsTemplate } from 'options';
|
||||||
|
import { TargetElement } from 'overlayscrollbars';
|
||||||
|
import { Environment } from 'environment';
|
||||||
|
|
||||||
|
let ENVIRONMENT: Environment;
|
||||||
|
|
||||||
|
interface UpdateHints {
|
||||||
|
_changedOptions: Options;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OverlayScrollbarsInstanceVars {
|
||||||
|
_documentElm: Document;
|
||||||
|
_windowElm: Window;
|
||||||
|
_htmlElm: HTMLElement;
|
||||||
|
_bodyElm: HTMLElement;
|
||||||
|
_targetElm: TargetElement;
|
||||||
|
_isTextarea: boolean;
|
||||||
|
_isBody: boolean;
|
||||||
|
_currentOptions: Options;
|
||||||
|
_setOptions(newOptions: Options): Options;
|
||||||
|
_update(updateHints: UpdateHints): void;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
const initSingletons = () => {
|
||||||
|
if (!ENVIRONMENT) {
|
||||||
|
ENVIRONMENT = new Environment();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export class OverlayScrollbars {
|
||||||
|
#instanceVars: OverlayScrollbarsInstanceVars = {
|
||||||
|
_setOptions(newOptions: Options): Options {
|
||||||
|
const { _currentOptions } = this;
|
||||||
|
const { validated } = validate(newOptions, optionsTemplate, _currentOptions, true);
|
||||||
|
|
||||||
|
this._currentOptions = assignDeep({}, _currentOptions, validated);
|
||||||
|
|
||||||
|
return validated;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(target: HTMLElement, options: Options) {
|
||||||
|
this.#instanceVars._documentElm = document;
|
||||||
|
this.#instanceVars._windowElm = window;
|
||||||
|
this.#instanceVars._htmlElm = document.body;
|
||||||
|
this.#instanceVars._bodyElm = document.body;
|
||||||
|
this.#instanceVars._targetElm = document.body;
|
||||||
|
this.#instanceVars._isTextarea = false;
|
||||||
|
this.#instanceVars._isBody = false;
|
||||||
|
initSingletons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export * from 'overlayscrollbars';
|
||||||
|
|
||||||
|
export type TargetElement = HTMLElement | HTMLTextAreaElement;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { OverlayScrollbarsLifecycle } from 'overlayscrollbars/lifecycles';
|
||||||
|
|
||||||
|
export interface StructureLifecycleOptions {
|
||||||
|
_paddingAbsolute: boolean;
|
||||||
|
_autoSizeCapable: boolean;
|
||||||
|
_heightAuto: boolean;
|
||||||
|
_widthAuto: boolean;
|
||||||
|
_border: [number, number, number, number];
|
||||||
|
_padding: [number, number, number, number];
|
||||||
|
_margin: [number, number, number, number];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StructureLifecycle extends OverlayScrollbarsLifecycle<StructureLifecycleOptions> {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
_update(options?: StructureLifecycleOptions): void {}
|
||||||
|
// eslint-disable-next-line
|
||||||
|
_destruct(): void {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { PlainObject } from 'typings';
|
||||||
|
import { Environment } from 'environment';
|
||||||
|
|
||||||
|
export abstract class OverlayScrollbarsLifecycle<T extends PlainObject> {
|
||||||
|
protected environment: Environment;
|
||||||
|
|
||||||
|
constructor(environment: Environment) {
|
||||||
|
this.environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract _update(options?: T): void;
|
||||||
|
|
||||||
|
abstract _destruct(): void;
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { createDOM, style, appendChildren, offsetSize, scrollLeft, scrollTop, jsAPI, addClass, each } from 'support';
|
||||||
|
|
||||||
|
const animationStartEventName = 'animationstart mozAnimationStart webkitAnimationStart MSAnimationStart';
|
||||||
|
const scrollEventName = 'scroll';
|
||||||
|
const scrollAmount = 3333333;
|
||||||
|
const ResizeObserverConstructor = jsAPI('ResizeObserver');
|
||||||
|
const classNameSizeObserver = 'os-size-observer';
|
||||||
|
const classNameSizeObserverListener = `${classNameSizeObserver}-listener`;
|
||||||
|
const classNameSizeObserverListenerItem = `${classNameSizeObserverListener}-item`;
|
||||||
|
const classNameSizeObserverListenerItemFinal = `${classNameSizeObserverListenerItem}-final`;
|
||||||
|
const cAF = cancelAnimationFrame;
|
||||||
|
const rAF = requestAnimationFrame;
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// 1. handling for event listeners (animationStartEventName.split(' '))
|
||||||
|
// 2. return not just element but also destruction function
|
||||||
|
// 3. shorthand handling for preventDefault & stopPropagation etc.
|
||||||
|
// 4. add test for appearance (display: none => display: block)
|
||||||
|
// 5. add functionality & tests for direction change
|
||||||
|
// 6. MAYBE add comparison function to offsetSize etc.
|
||||||
|
// 7. Create test utils (waitFor)
|
||||||
|
|
||||||
|
export const createSizeObserver = (onSizeChangedCallback: () => void) => {
|
||||||
|
const baseElements = createDOM(`<div class="${classNameSizeObserver}"><div class="${classNameSizeObserverListener}"></div></div>`);
|
||||||
|
const sizeObserver = baseElements[0] as HTMLElement;
|
||||||
|
const listenerElement = sizeObserver.firstChild as HTMLElement;
|
||||||
|
if (ResizeObserverConstructor) {
|
||||||
|
addClass(sizeObserver, 'resize-observer');
|
||||||
|
const resizeObserverInstance = new ResizeObserverConstructor(onSizeChangedCallback);
|
||||||
|
resizeObserverInstance.observe(listenerElement);
|
||||||
|
} else {
|
||||||
|
const observerElementChildren = createDOM(
|
||||||
|
`<div class="${classNameSizeObserverListenerItem}" dir="ltr"><div class="${classNameSizeObserverListenerItem}"><div class="${classNameSizeObserverListenerItemFinal}"></div></div><div class="${classNameSizeObserverListenerItem}"><div class="${classNameSizeObserverListenerItemFinal}" style="width: 200%; height: 200%"></div></div></div>`
|
||||||
|
);
|
||||||
|
appendChildren(listenerElement, observerElementChildren);
|
||||||
|
const observerElementChildrenRoot = observerElementChildren[0] as HTMLElement;
|
||||||
|
const shrinkElement = observerElementChildrenRoot.lastChild as HTMLElement;
|
||||||
|
const expandElement = observerElementChildrenRoot.firstChild as HTMLElement;
|
||||||
|
const expandElementChild = expandElement?.firstChild as HTMLElement;
|
||||||
|
|
||||||
|
let cacheSize = offsetSize(listenerElement);
|
||||||
|
let currSize = cacheSize;
|
||||||
|
let isDirty = false;
|
||||||
|
let rAFId: number;
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
scrollLeft(expandElement, scrollAmount);
|
||||||
|
scrollTop(expandElement, scrollAmount);
|
||||||
|
scrollLeft(shrinkElement, scrollAmount);
|
||||||
|
scrollTop(shrinkElement, scrollAmount);
|
||||||
|
};
|
||||||
|
const onResized = function () {
|
||||||
|
rAFId = 0;
|
||||||
|
if (!isDirty) return;
|
||||||
|
|
||||||
|
cacheSize = currSize;
|
||||||
|
onSizeChangedCallback();
|
||||||
|
};
|
||||||
|
const onScroll = (scrollEvent?: Event) => {
|
||||||
|
currSize = offsetSize(listenerElement);
|
||||||
|
isDirty = currSize.w !== cacheSize.w || currSize.h !== cacheSize.h;
|
||||||
|
|
||||||
|
if (scrollEvent && isDirty && !rAFId) {
|
||||||
|
cAF(rAFId);
|
||||||
|
rAFId = rAF(onResized);
|
||||||
|
} else if (!scrollEvent) onResized();
|
||||||
|
|
||||||
|
reset();
|
||||||
|
if (scrollEvent) {
|
||||||
|
scrollEvent.preventDefault();
|
||||||
|
scrollEvent.stopPropagation();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
expandElement.addEventListener(scrollEventName, onScroll);
|
||||||
|
shrinkElement.addEventListener(scrollEventName, onScroll);
|
||||||
|
each(animationStartEventName.split(' '), (eventName) => {
|
||||||
|
sizeObserver.addEventListener(eventName, () => {
|
||||||
|
onScroll();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// lets assume that the divs will never be that large and a constant value is enough
|
||||||
|
style(expandElementChild, {
|
||||||
|
width: scrollAmount,
|
||||||
|
height: scrollAmount,
|
||||||
|
});
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sizeObserver;
|
||||||
|
};
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
$scrollbar-cushion: 100px;
|
||||||
|
|
||||||
|
.os-size-observer,
|
||||||
|
.os-size-observer-listener {
|
||||||
|
padding: inherit;
|
||||||
|
margin: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
overflow: hidden;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-size-observer,
|
||||||
|
.os-size-observer-listener,
|
||||||
|
.os-size-observer-listener-item,
|
||||||
|
.os-size-observer-listener-item-final {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-size-observer {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
animation-duration: 0.001s;
|
||||||
|
animation-name: os-size-observer-appear-animation;
|
||||||
|
|
||||||
|
&.resize-observer {
|
||||||
|
.os-size-observer-listener {
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-size-observer-listener {
|
||||||
|
display: block;
|
||||||
|
height: 200%;
|
||||||
|
width: 200%;
|
||||||
|
box-sizing: content-box;
|
||||||
|
|
||||||
|
// lets assume no scrollbar is 100px wide
|
||||||
|
& > .os-size-observer-listener-item {
|
||||||
|
top: -$scrollbar-cushion;
|
||||||
|
right: -$scrollbar-cushion;
|
||||||
|
bottom: -$scrollbar-cushion;
|
||||||
|
left: -$scrollbar-cushion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-size-observer-listener-item {
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
direction: ltr;
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-size-observer-listener-item-final {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes os-size-observer-appear-animation {
|
||||||
|
from {
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
body {
|
|
||||||
background: blue;
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
$c: red;
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: $c;
|
|
||||||
}
|
|
||||||
@@ -19,12 +19,9 @@ export type OptionsTemplate<T extends Required<T>> = {
|
|||||||
? OptionsTemplateValue<T[P]>
|
? OptionsTemplateValue<T[P]>
|
||||||
: never;
|
: never;
|
||||||
};
|
};
|
||||||
export type OptionsValidated<T> = {
|
|
||||||
[P in keyof T]?: OptionsValidated<T[P]>;
|
|
||||||
};
|
|
||||||
export type OptionsValidatedResult<T> = {
|
export type OptionsValidatedResult<T> = {
|
||||||
readonly foreign: PlainObject;
|
readonly foreign: PlainObject;
|
||||||
readonly validated: OptionsValidated<T>;
|
readonly validated: T;
|
||||||
};
|
};
|
||||||
// Options With Options Template Typings:
|
// Options With Options Template Typings:
|
||||||
export type OptionsAndOptionsTemplateValue<T extends OptionsTemplateNativeTypes> = [T, OptionsTemplateValue<T>];
|
export type OptionsAndOptionsTemplateValue<T extends OptionsTemplateNativeTypes> = [T, OptionsTemplateValue<T>];
|
||||||
@@ -48,5 +45,5 @@ type OptionsTemplateValueNonEnum<T extends OptionsTemplateNativeTypes> =
|
|||||||
| OptionsTemplateType<T>
|
| OptionsTemplateType<T>
|
||||||
| [OptionsTemplateType<T>, ...Array<OptionsTemplateTypes>];
|
| [OptionsTemplateType<T>, ...Array<OptionsTemplateTypes>];
|
||||||
type ExtractPropsKey<T, TProps extends T[keyof T]> = {
|
type ExtractPropsKey<T, TProps extends T[keyof T]> = {
|
||||||
[P in keyof T]: TProps extends T[P] ? P : never;
|
[P in keyof T]: TProps extends T[P] ? P : never;
|
||||||
}[keyof T];
|
}[keyof T];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { each, indexOf, hasOwnProperty, keys } from 'support/utils';
|
import { each, indexOf, hasOwnProperty, keys } from 'support/utils';
|
||||||
import { type, isArray, isUndefined, isEmptyObject, isPlainObject, isString } from 'support/utils/types';
|
import { type, isArray, isUndefined, isEmptyObject, isPlainObject, isString } from 'support/utils/types';
|
||||||
import { OptionsTemplate, OptionsTemplateTypes, OptionsTemplateType, OptionsValidated, Func, OptionsValidatedResult } from 'support/options';
|
import { OptionsTemplate, OptionsTemplateTypes, OptionsTemplateType, Func, OptionsValidatedResult } from 'support/options';
|
||||||
import { PlainObject } from 'typings';
|
import { PlainObject } from 'typings';
|
||||||
|
|
||||||
const { stringify } = JSON;
|
const { stringify } = JSON;
|
||||||
@@ -43,11 +43,11 @@ const optionsTemplateTypes: OptionsTemplateTypesDictionary = ['boolean', 'number
|
|||||||
const validateRecursive = <T extends PlainObject>(
|
const validateRecursive = <T extends PlainObject>(
|
||||||
options: T,
|
options: T,
|
||||||
template: OptionsTemplate<Required<T>>,
|
template: OptionsTemplate<Required<T>>,
|
||||||
optionsDiff: OptionsValidated<T>,
|
optionsDiff: T,
|
||||||
doWriteErrors?: boolean,
|
doWriteErrors?: boolean,
|
||||||
propPath?: string
|
propPath?: string
|
||||||
): OptionsValidatedResult<T> => {
|
): OptionsValidatedResult<T> => {
|
||||||
const validatedOptions: OptionsValidated<T> = {};
|
const validatedOptions: T = {} as T;
|
||||||
const optionsCopy: T = { ...options };
|
const optionsCopy: T = { ...options };
|
||||||
const props = keys(template).filter((prop) => hasOwnProperty(options, prop));
|
const props = keys(template).filter((prop) => hasOwnProperty(options, prop));
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ const validateRecursive = <T extends PlainObject>(
|
|||||||
const validate = <T extends PlainObject>(
|
const validate = <T extends PlainObject>(
|
||||||
options: T,
|
options: T,
|
||||||
template: OptionsTemplate<Required<T>>,
|
template: OptionsTemplate<Required<T>>,
|
||||||
optionsDiff?: OptionsValidated<T>,
|
optionsDiff?: T,
|
||||||
doWriteErrors?: boolean
|
doWriteErrors?: boolean
|
||||||
): OptionsValidatedResult<T> => {
|
): OptionsValidatedResult<T> => {
|
||||||
/*
|
/*
|
||||||
@@ -155,7 +155,7 @@ const validate = <T extends PlainObject>(
|
|||||||
Object.assign(result.validated, foreign);
|
Object.assign(result.validated, foreign);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
return validateRecursive(options, template, optionsDiff || {}, doWriteErrors || false);
|
return validateRecursive<T>(options, template, optionsDiff || ({} as T), doWriteErrors || false);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { validate, optionsTemplateTypes };
|
export { validate, optionsTemplateTypes };
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Environment } from 'environment';
|
||||||
import url from './.build/build.html';
|
import url from './.build/build.html';
|
||||||
|
|
||||||
describe('Environment', () => {
|
describe('Environment', () => {
|
||||||
@@ -6,10 +7,13 @@ describe('Environment', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be titled "Environment"', async () => {
|
it('should be titled "Environment"', async () => {
|
||||||
await expect(page).toMatchElement('#a');
|
//await expect(page).toMatchElement('#a');
|
||||||
await expect(page).toMatchElement('#b');
|
//await expect(page).toMatchElement('#b');
|
||||||
await expect(page).toMatchElement('#c');
|
//await expect(page).toMatchElement('#c');
|
||||||
await expect(page).toMatchElement('#d');
|
//await expect(page).toMatchElement('#d');
|
||||||
|
|
||||||
|
const a: Environment = await page.evaluate(() => window.envInstance);
|
||||||
|
console.log(a);
|
||||||
await expect(page.title()).resolves.toMatch('Environment');
|
await expect(page.title()).resolves.toMatch('Environment');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import 'overlayscrollbars.scss';
|
||||||
import { Environment } from 'environment';
|
import { Environment } from 'environment';
|
||||||
import 'some.scss';
|
|
||||||
import 'some.css';
|
|
||||||
|
|
||||||
document.body.append(JSON.stringify(new Environment()));
|
window.envInstance = new Environment();
|
||||||
|
document.body.textContent = JSON.stringify(window.envInstance);
|
||||||
|
|
||||||
export { Environment };
|
export { Environment };
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<label for="height">height</label>
|
||||||
|
<select name="height" id="height">
|
||||||
|
<option value="heightAuto">auto</option>
|
||||||
|
<option value="heightHundred">100%</option>
|
||||||
|
<option value="height200">200px</option>
|
||||||
|
</select>
|
||||||
|
<label for="width">width</label>
|
||||||
|
<select name="width" id="width">
|
||||||
|
<option value="widthAuto">auto</option>
|
||||||
|
<option value="widthHundred">100%</option>
|
||||||
|
<option value="width200">200px</option>
|
||||||
|
</select>
|
||||||
|
<label for="padding">padding</label>
|
||||||
|
<select name="padding" id="padding">
|
||||||
|
<option value="padding0">0</option>
|
||||||
|
<option value="padding10">10px</option>
|
||||||
|
<option value="padding50">50px</option>
|
||||||
|
</select>
|
||||||
|
<button id="start">start</button>
|
||||||
|
<span>Detected resizes: <span id="resizes">0</span></span>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div id="target"></div>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#target {
|
||||||
|
border: 2px solid red;
|
||||||
|
overflow: scroll;
|
||||||
|
resize: both;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding0 {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.padding10 {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.padding50 {
|
||||||
|
padding: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heightAuto {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
.height200 {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
.heightHundred {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widthAuto {
|
||||||
|
width: auto;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.width200 {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
.widthHundred {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import expectPuppeteer from 'expect-puppeteer';
|
||||||
|
import url from './.build/build.html';
|
||||||
|
|
||||||
|
describe('Environment', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await page.goto(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test', async () => {
|
||||||
|
await expectPuppeteer(page).toClick('#start');
|
||||||
|
await expectPuppeteer(page).toMatchElement('#testResult.passed', {
|
||||||
|
timeout: 30000,
|
||||||
|
});
|
||||||
|
}, 30000);
|
||||||
|
});
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
import 'overlayscrollbars.scss';
|
||||||
|
import './index.scss';
|
||||||
|
import { createSizeObserver } from 'overlayscrollbars/observers/createSizeObserver';
|
||||||
|
import { from, removeClass, addClass } from 'support';
|
||||||
|
|
||||||
|
const targetElm = document.querySelector('#target');
|
||||||
|
const heightSelect: HTMLSelectElement | null = document.querySelector('#height');
|
||||||
|
const widthSelect: HTMLSelectElement | null = document.querySelector('#width');
|
||||||
|
const paddingSelect: HTMLSelectElement | null = document.querySelector('#padding');
|
||||||
|
const startBtn: HTMLButtonElement | null = document.querySelector('#start');
|
||||||
|
const resizesSlot: HTMLButtonElement | null = document.querySelector('#resizes');
|
||||||
|
|
||||||
|
const getSelectOptions = (selectElement: HTMLSelectElement) => {
|
||||||
|
const arr = from(selectElement.options).map((option) => option.value);
|
||||||
|
return arr;
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectCallback = (event: Event) => {
|
||||||
|
const target = event.target as HTMLSelectElement;
|
||||||
|
const selectedOption = target.value;
|
||||||
|
const selectOptions = getSelectOptions(target);
|
||||||
|
|
||||||
|
removeClass(targetElm, selectOptions.join(' '));
|
||||||
|
addClass(targetElm, selectedOption);
|
||||||
|
};
|
||||||
|
|
||||||
|
heightSelect?.addEventListener('change', selectCallback);
|
||||||
|
widthSelect?.addEventListener('change', selectCallback);
|
||||||
|
paddingSelect?.addEventListener('change', selectCallback);
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
selectCallback({ target: heightSelect });
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
selectCallback({ target: widthSelect });
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
selectCallback({ target: paddingSelect });
|
||||||
|
|
||||||
|
let iterations = 0;
|
||||||
|
const observerElm = createSizeObserver(() => {
|
||||||
|
iterations += 1;
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (resizesSlot) {
|
||||||
|
resizesSlot.textContent = iterations.toString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
targetElm?.appendChild(observerElm);
|
||||||
|
|
||||||
|
const waitFor = (func: () => any) => {
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
if (func()) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
if (now - start > 5000) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
window.setTestResult(false);
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
}, 30);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const iterateSelect = async (select: HTMLSelectElement | null, afterEach?: () => any) => {
|
||||||
|
if (select) {
|
||||||
|
const selectOptions = getSelectOptions(select);
|
||||||
|
const selectOptionsReversed = getSelectOptions(select).reverse();
|
||||||
|
const iterateOptions = [...selectOptions, ...selectOptionsReversed];
|
||||||
|
for (let i = 0; i < iterateOptions.length; i++) {
|
||||||
|
const option = iterateOptions[i];
|
||||||
|
const currValue = select.value;
|
||||||
|
if (option === currValue) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
select.value = option;
|
||||||
|
const currIterations = iterations;
|
||||||
|
|
||||||
|
let event;
|
||||||
|
if (typeof Event === 'function') {
|
||||||
|
event = new Event('change');
|
||||||
|
} else {
|
||||||
|
event = document.createEvent('Event');
|
||||||
|
event.initEvent('change', true, true);
|
||||||
|
}
|
||||||
|
select.dispatchEvent(event);
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
await waitFor(() => iterations === currIterations + 1);
|
||||||
|
|
||||||
|
if (typeof afterEach === 'function') {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
await afterEach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.iteratePadding = async (afterEach?: () => any) => {
|
||||||
|
await iterateSelect(paddingSelect, afterEach);
|
||||||
|
};
|
||||||
|
window.iterateHeight = async (afterEach?: () => any) => {
|
||||||
|
await iterateSelect(heightSelect, afterEach);
|
||||||
|
};
|
||||||
|
window.iterateWidth = async (afterEach?: () => any) => {
|
||||||
|
await iterateSelect(widthSelect, afterEach);
|
||||||
|
};
|
||||||
|
|
||||||
|
const start = (window.iterate = async () => {
|
||||||
|
window.setTestResult(null);
|
||||||
|
targetElm?.removeAttribute('style');
|
||||||
|
await iterateHeight(async () => {
|
||||||
|
await iterateWidth(async () => {
|
||||||
|
await iteratePadding();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
window.setTestResult(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
startBtn?.addEventListener('click', start);
|
||||||
@@ -2,16 +2,16 @@ import { XY } from 'support';
|
|||||||
declare type OnEnvironmentChanged = (env: Environment) => void;
|
declare type OnEnvironmentChanged = (env: Environment) => void;
|
||||||
export declare class Environment {
|
export declare class Environment {
|
||||||
#private;
|
#private;
|
||||||
autoUpdateLoop: boolean;
|
_autoUpdateLoop: boolean;
|
||||||
nativeScrollbarSize: XY;
|
_nativeScrollbarSize: XY;
|
||||||
nativeScrollbarIsOverlaid: XY<boolean>;
|
_nativeScrollbarIsOverlaid: XY<boolean>;
|
||||||
nativeScrollbarStyling: boolean;
|
_nativeScrollbarStyling: boolean;
|
||||||
rtlScrollBehavior: {
|
_rtlScrollBehavior: {
|
||||||
n: boolean;
|
n: boolean;
|
||||||
i: boolean;
|
i: boolean;
|
||||||
};
|
};
|
||||||
supportPassiveEvents: boolean;
|
_supportPassiveEvents: boolean;
|
||||||
supportResizeObserver: boolean;
|
_supportResizeObserver: boolean;
|
||||||
constructor();
|
constructor();
|
||||||
addListener(listener: OnEnvironmentChanged): void;
|
addListener(listener: OnEnvironmentChanged): void;
|
||||||
removeListener(listener: OnEnvironmentChanged): void;
|
removeListener(listener: OnEnvironmentChanged): void;
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from 'overlayscrollbars';
|
||||||
|
export declare type TargetElement = HTMLElement | HTMLTextAreaElement;
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
import { OverlayScrollbarsLifecycle } from 'overlayscrollbars/lifecycles';
|
||||||
|
export interface StructureLifecycleOptions {
|
||||||
|
_paddingAbsolute: boolean;
|
||||||
|
_autoSizeCapable: boolean;
|
||||||
|
_heightAuto: boolean;
|
||||||
|
_widthAuto: boolean;
|
||||||
|
_border: [number, number, number, number];
|
||||||
|
_padding: [number, number, number, number];
|
||||||
|
_margin: [number, number, number, number];
|
||||||
|
}
|
||||||
|
export declare class StructureLifecycle extends OverlayScrollbarsLifecycle<StructureLifecycleOptions> {
|
||||||
|
_update(options?: StructureLifecycleOptions): void;
|
||||||
|
_destruct(): void;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { PlainObject } from 'typings';
|
||||||
|
import { Environment } from 'environment';
|
||||||
|
export declare abstract class OverlayScrollbarsLifecycle<T extends PlainObject> {
|
||||||
|
protected environment: Environment;
|
||||||
|
constructor(environment: Environment);
|
||||||
|
abstract _update(options?: T): void;
|
||||||
|
abstract _destruct(): void;
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export declare const createSizeObserver: (onSizeChangedCallback: () => void) => HTMLElement;
|
||||||
@@ -9,12 +9,9 @@ export declare type OptionsTemplateValue<T extends OptionsTemplateNativeTypes =
|
|||||||
export declare type OptionsTemplate<T extends Required<T>> = {
|
export declare type OptionsTemplate<T extends Required<T>> = {
|
||||||
[P in keyof T]: PlainObject extends T[P] ? OptionsTemplate<Required<T[P]>> : T[P] extends OptionsTemplateNativeTypes ? OptionsTemplateValue<T[P]> : never;
|
[P in keyof T]: PlainObject extends T[P] ? OptionsTemplate<Required<T[P]>> : T[P] extends OptionsTemplateNativeTypes ? OptionsTemplateValue<T[P]> : never;
|
||||||
};
|
};
|
||||||
export declare type OptionsValidated<T> = {
|
|
||||||
[P in keyof T]?: OptionsValidated<T[P]>;
|
|
||||||
};
|
|
||||||
export declare type OptionsValidatedResult<T> = {
|
export declare type OptionsValidatedResult<T> = {
|
||||||
readonly foreign: PlainObject;
|
readonly foreign: PlainObject;
|
||||||
readonly validated: OptionsValidated<T>;
|
readonly validated: T;
|
||||||
};
|
};
|
||||||
export declare type OptionsAndOptionsTemplateValue<T extends OptionsTemplateNativeTypes> = [T, OptionsTemplateValue<T>];
|
export declare type OptionsAndOptionsTemplateValue<T extends OptionsTemplateNativeTypes> = [T, OptionsTemplateValue<T>];
|
||||||
export declare type OptionsAndOptionsTemplate<T extends Required<T>> = {
|
export declare type OptionsAndOptionsTemplate<T extends Required<T>> = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { OptionsTemplate, OptionsTemplateType, OptionsValidated, Func, OptionsValidatedResult } from 'support/options';
|
import { OptionsTemplate, OptionsTemplateType, Func, OptionsValidatedResult } from 'support/options';
|
||||||
import { PlainObject } from 'typings';
|
import { PlainObject } from 'typings';
|
||||||
declare const optionsTemplateTypes: OptionsTemplateTypesDictionary;
|
declare const optionsTemplateTypes: OptionsTemplateTypesDictionary;
|
||||||
declare const validate: <T extends PlainObject<any>>(options: T, template: OptionsTemplate<Required<T>>, optionsDiff?: OptionsValidated<T> | undefined, doWriteErrors?: boolean | undefined) => OptionsValidatedResult<T>;
|
declare const validate: <T extends PlainObject<any>>(options: T, template: OptionsTemplate<Required<T>>, optionsDiff?: T | undefined, doWriteErrors?: boolean | undefined) => OptionsValidatedResult<T>;
|
||||||
export { validate, optionsTemplateTypes };
|
export { validate, optionsTemplateTypes };
|
||||||
declare type OptionsTemplateTypesDictionary = {
|
declare type OptionsTemplateTypesDictionary = {
|
||||||
readonly boolean: OptionsTemplateType<boolean>;
|
readonly boolean: OptionsTemplateType<boolean>;
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ const resolvePath = (basePath, pathToResolve, appendExt) => {
|
|||||||
|
|
||||||
const resolveConfig = (config) => {
|
const resolveConfig = (config) => {
|
||||||
if (typeof config === 'function') {
|
if (typeof config === 'function') {
|
||||||
return config(rollupConfigDefaults) || {};
|
return config(rollupConfigDefaults, legacyBabelConfig, esmBabelConfig) || {};
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
@@ -164,6 +164,12 @@ const rollupConfig = (config = {}, { project = process.cwd(), overwrite = {}, si
|
|||||||
rollupTerser({
|
rollupTerser({
|
||||||
ecma: 8,
|
ecma: 8,
|
||||||
safari10: true,
|
safari10: true,
|
||||||
|
mangle: {
|
||||||
|
safari10: true,
|
||||||
|
properties: {
|
||||||
|
regex: /^_/,
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2543,6 +2543,11 @@ core-js@^2.6.5:
|
|||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
|
||||||
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
|
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
|
||||||
|
|
||||||
|
core-js@^3.6.5:
|
||||||
|
version "3.6.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a"
|
||||||
|
integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==
|
||||||
|
|
||||||
core-util-is@1.0.2, core-util-is@~1.0.0:
|
core-util-is@1.0.2, core-util-is@~1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||||
|
|||||||
Reference in New Issue
Block a user