improve code, add env, test nesting

This commit is contained in:
Rene
2022-07-05 17:26:29 +02:00
parent 82151d6bc3
commit 26d87dd538
28 changed files with 959 additions and 499 deletions
+146 -117
View File
@@ -864,7 +864,7 @@ const createEnvironment = () => {
const envDOM = createDOM(`<div class="${classNameEnvironment}"><div></div></div>`);
const envElm = envDOM[0];
const envChildElm = envElm.firstChild;
const onChangedListener = new Set();
const [addEvent,, triggerEvent] = createEventListenerHub();
const [updateNativeScrollbarSizeCache, getNativeScrollbarSizeCache] = createCache({
_initialValue: getNativeScrollbarSize(body, envElm, envChildElm),
_equal: equalXY
@@ -884,12 +884,7 @@ const createEnvironment = () => {
_cssCustomProperties: style(envElm, 'zIndex') === '-1',
_rtlScrollBehavior: getRtlScrollBehavior(envElm, envChildElm),
_flexboxGlue: getFlexboxGlue(envElm, envChildElm),
_addListener(listener) {
onChangedListener.add(listener);
return () => onChangedListener.delete(listener);
},
_addListener: listener => addEvent('_', listener),
_getInitializationStrategy: assignDeep.bind(0, {}, initializationStrategy),
_setInitializationStrategy(newInitializationStrategy) {
@@ -912,40 +907,38 @@ const createEnvironment = () => {
let size = windowSize();
let dpr = getWindowDPR();
window.addEventListener('resize', () => {
if (onChangedListener.size) {
const sizeNew = windowSize();
const deltaSize = {
w: sizeNew.w - size.w,
h: sizeNew.h - size.h
};
if (deltaSize.w === 0 && deltaSize.h === 0) return;
const deltaAbsSize = {
w: abs(deltaSize.w),
h: abs(deltaSize.h)
};
const deltaAbsRatio = {
w: abs(round(sizeNew.w / (size.w / 100.0))),
h: abs(round(sizeNew.h / (size.h / 100.0)))
};
const dprNew = getWindowDPR();
const deltaIsBigger = deltaAbsSize.w > 2 && deltaAbsSize.h > 2;
const difference = !diffBiggerThanOne(deltaAbsRatio.w, deltaAbsRatio.h);
const dprChanged = dprNew !== dpr && dpr > 0;
const isZoom = deltaIsBigger && difference && dprChanged;
const sizeNew = windowSize();
const deltaSize = {
w: sizeNew.w - size.w,
h: sizeNew.h - size.h
};
if (deltaSize.w === 0 && deltaSize.h === 0) return;
const deltaAbsSize = {
w: abs(deltaSize.w),
h: abs(deltaSize.h)
};
const deltaAbsRatio = {
w: abs(round(sizeNew.w / (size.w / 100.0))),
h: abs(round(sizeNew.h / (size.h / 100.0)))
};
const dprNew = getWindowDPR();
const deltaIsBigger = deltaAbsSize.w > 2 && deltaAbsSize.h > 2;
const difference = !diffBiggerThanOne(deltaAbsRatio.w, deltaAbsRatio.h);
const dprChanged = dprNew !== dpr && dpr > 0;
const isZoom = deltaIsBigger && difference && dprChanged;
if (isZoom) {
const [scrollbarSize, scrollbarSizeChanged] = updateNativeScrollbarSizeCache(getNativeScrollbarSize(body, envElm, envChildElm));
assignDeep(environmentInstance._nativeScrollbarSize, scrollbarSize);
removeElements(envElm);
if (isZoom) {
const [scrollbarSize, scrollbarSizeChanged] = updateNativeScrollbarSizeCache(getNativeScrollbarSize(body, envElm, envChildElm));
assignDeep(environmentInstance._nativeScrollbarSize, scrollbarSize);
removeElements(envElm);
if (scrollbarSizeChanged) {
runEach(onChangedListener);
}
if (scrollbarSizeChanged) {
triggerEvent('_');
}
size = sizeNew;
dpr = dprNew;
}
size = sizeNew;
dpr = dprNew;
});
}
@@ -1115,14 +1108,18 @@ const createTrinsicUpdate = (structureSetupElements, state) => {
} = structureSetupElements;
const [getState] = state;
return updateHints => {
const {
_flexboxGlue
} = getEnvironment();
const {
_heightIntrinsic
} = getState();
const {
_heightIntrinsicChanged
} = updateHints;
const heightIntrinsicChanged = (_content || !_flexboxGlue) && _heightIntrinsicChanged;
if (_heightIntrinsicChanged) {
if (heightIntrinsicChanged) {
style(_content, {
height: _heightIntrinsic ? '' : '100%',
display: _heightIntrinsic ? '' : 'inline'
@@ -1130,8 +1127,8 @@ const createTrinsicUpdate = (structureSetupElements, state) => {
}
return {
_sizeChanged: _heightIntrinsicChanged,
_contentMutation: _heightIntrinsicChanged
_sizeChanged: heightIntrinsicChanged,
_contentMutation: heightIntrinsicChanged
};
};
};
@@ -1208,6 +1205,7 @@ const {
max
} = Math;
const strVisible = 'visible';
const strHidden = 'hidden';
const overlaidScrollbarsHideOffset = 42;
const whCacheOptions = {
_equal: equalWH,
@@ -1219,8 +1217,8 @@ const whCacheOptions = {
const xyCacheOptions = {
_equal: equalXY,
_initialValue: {
x: false,
y: false
x: strHidden,
y: strHidden
}
};
@@ -1258,7 +1256,7 @@ const createOverflowUpdate = (structureSetupElements, state) => {
const [updateSizeFraction, getCurrentSizeFraction] = createCache(whCacheOptions, fractionalSize.bind(0, _host));
const [updateViewportScrollSizeCache, getCurrentViewportScrollSizeCache] = createCache(whCacheOptions, scrollSize.bind(0, _viewport));
const [updateOverflowAmountCache, getCurrentOverflowAmountCache] = createCache(whCacheOptions);
const [updateOverflowScrollCache] = createCache(xyCacheOptions);
const [updateOverflowStyleCache] = createCache(xyCacheOptions);
const fixFlexboxGlue = (viewportOverflowState, heightIntrinsic) => {
style(_viewport, {
@@ -1286,39 +1284,45 @@ const createOverflowUpdate = (structureSetupElements, state) => {
};
const getViewportOverflowState = (showNativeOverlaidScrollbars, viewportStyleObj) => {
const {
x: overlaidX,
y: overlaidY
} = _nativeScrollbarIsOverlaid;
const determineOverflow = !viewportStyleObj;
const arrangeHideOffset = !_nativeScrollbarStyling && !showNativeOverlaidScrollbars ? overlaidScrollbarsHideOffset : 0;
const styleObj = determineOverflow ? style(_viewport, ['overflowX', 'overflowY']) : viewportStyleObj;
const scroll = {
x: styleObj.overflowX === 'scroll',
y: styleObj.overflowY === 'scroll'
};
const nonScrollbarStylingHideOffset = {
x: overlaidX ? arrangeHideOffset : _nativeScrollbarSize.x,
y: overlaidY ? arrangeHideOffset : _nativeScrollbarSize.y
};
const scrollbarsHideOffset = {
x: scroll.x && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset.x : 0,
y: scroll.y && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset.y : 0
const getStatePerAxis = (styleKey, isOverlaid, nativeScrollbarSize) => {
const overflowStyle = style(_viewport, styleKey);
const objectPrefferedOverflowStyle = viewportStyleObj ? viewportStyleObj[styleKey] : overflowStyle;
const overflowScroll = objectPrefferedOverflowStyle === 'scroll';
const nonScrollbarStylingHideOffset = isOverlaid ? arrangeHideOffset : nativeScrollbarSize;
const scrollbarsHideOffset = overflowScroll && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset : 0;
const scrollbarsHideOffsetArrange = isOverlaid && !!arrangeHideOffset;
return [overflowStyle, overflowScroll, scrollbarsHideOffset, scrollbarsHideOffsetArrange];
};
const [xOverflowStyle, xOverflowScroll, xScrollbarsHideOffset, xScrollbarsHideOffsetArrange] = getStatePerAxis('overflowX', _nativeScrollbarIsOverlaid.x, _nativeScrollbarSize.x);
const [yOverflowStyle, yOverflowScroll, yScrollbarsHideOffset, yScrollbarsHideOffsetArrange] = getStatePerAxis('overflowY', _nativeScrollbarIsOverlaid.y, _nativeScrollbarSize.y);
return {
_overflowScroll: scroll,
_scrollbarsHideOffsetArrange: {
x: overlaidX && !!arrangeHideOffset,
y: overlaidY && !!arrangeHideOffset
_overflowStyle: {
x: xOverflowStyle,
y: yOverflowStyle
},
_scrollbarsHideOffset: scrollbarsHideOffset
_overflowScroll: {
x: xOverflowScroll,
y: yOverflowScroll
},
_scrollbarsHideOffset: {
x: xScrollbarsHideOffset,
y: yScrollbarsHideOffset
},
_scrollbarsHideOffsetArrange: {
x: xScrollbarsHideOffsetArrange,
y: yScrollbarsHideOffsetArrange
}
};
};
const setViewportOverflowState = (showNativeOverlaidScrollbars, hasOverflow, overflowOption, viewportStyleObj) => {
const setAxisOverflowStyle = (behavior, hasOverflowAxis) => {
const overflowVisible = overflowIsVisible(behavior);
return [hasOverflowAxis && !overflowVisible ? behavior : '', hasOverflowAxis && overflowVisible && behavior.replace(`${strVisible}-`, '') || ''];
const overflowVisibleBehavior = hasOverflowAxis && overflowVisible && behavior.replace(`${strVisible}-`, '') || '';
return [hasOverflowAxis && !overflowVisible ? behavior : '', overflowIsVisible(overflowVisibleBehavior) ? 'hidden' : overflowVisibleBehavior];
};
const [overflowX, visibleBehaviorX] = setAxisOverflowStyle(overflowOption.x, hasOverflow.x);
@@ -1486,7 +1490,6 @@ const createOverflowUpdate = (structureSetupElements, state) => {
let sizeFractionCache = getCurrentSizeFraction(force);
let viewportScrollSizeCache = getCurrentViewportScrollSizeCache(force);
let overflowAmuntCache = getCurrentOverflowAmountCache(force);
let updateHintsReturn;
let preMeasureViewportOverflowState;
if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarStyling) {
@@ -1498,11 +1501,11 @@ const createOverflowUpdate = (structureSetupElements, state) => {
fixFlexboxGlue(preMeasureViewportOverflowState, _heightIntrinsic);
}
if (overflowVisible) {
removeClass(_viewport, classNameOverflowVisible);
}
if (_sizeChanged || _paddingStyleChanged || _contentMutation || _directionChanged || showNativeOverlaidScrollbarsChanged) {
if (overflowVisible) {
removeClass(_viewport, classNameOverflowVisible);
}
const [redoViewportArrange, undoViewportArrangeOverflowState] = undoViewportArrange(showNativeOverlaidScrollbars, _directionIsRTL, preMeasureViewportOverflowState);
const [_sizeFraction, _sizeFractionChanged] = sizeFractionCache = updateSizeFraction(force);
const [_viewportScrollSize, _viewportScrollSizeChanged] = viewportScrollSizeCache = updateViewportScrollSizeCache(force);
@@ -1545,7 +1548,6 @@ const createOverflowUpdate = (structureSetupElements, state) => {
};
const viewportOverflowState = setViewportOverflowState(showNativeOverlaidScrollbars, hasOverflow, overflow, viewportStyle);
const viewportArranged = arrangeViewport(viewportOverflowState, viewportScrollSize, sizeFraction, _directionIsRTL);
const [overflowScroll, overflowScrollChanged] = updateOverflowScrollCache(viewportOverflowState._overflowScroll);
hideNativeScrollbars(viewportOverflowState, _directionIsRTL, viewportArranged, viewportStyle);
if (adjustFlexboxGlue) {
@@ -1553,21 +1555,24 @@ const createOverflowUpdate = (structureSetupElements, state) => {
}
style(_viewport, viewportStyle);
setState({
_overflowScroll: overflowScroll,
_overflowAmount: overflowAmount,
_hasOverflow: hasOverflow
});
updateHintsReturn = {
_overflowAmountChanged: overflowAmountChanged,
_overflowScrollChanged: overflowScrollChanged
};
}
attr(_host, dataAttributeHost, removeClipping ? 'overflowVisible' : '');
conditionalClass(_padding, classNameOverflowVisible, removeClipping);
conditionalClass(_viewport, classNameOverflowVisible, overflowVisible);
return updateHintsReturn;
const [overflowStyle, overflowStyleChanged] = updateOverflowStyleCache(getViewportOverflowState(showNativeOverlaidScrollbars)._overflowStyle);
setState({
_overflowStyle: overflowStyle,
_overflowAmount: {
x: overflowAmount.w,
y: overflowAmount.h
},
_hasOverflow: hasOverflow
});
return {
_overflowStyleChanged: overflowStyleChanged,
_overflowAmountChanged: overflowAmountChanged
};
};
};
@@ -1600,8 +1605,8 @@ const createStructureSetupUpdate = (structureSetupElements, state) => {
_paddingStyleChanged: false,
_directionChanged: false,
_heightIntrinsicChanged: false,
_overflowScrollChanged: false,
_overflowAmountChanged: false,
_overflowStyleChanged: false,
_hostMutation: false,
_contentMutation: false
}, updateHints), {}, force);
@@ -2178,12 +2183,12 @@ const initialStructureSetupUpdateState = {
paddingLeft: 0
},
_overflowAmount: {
w: 0,
h: 0
x: 0,
y: 0
},
_overflowScroll: {
x: false,
y: false
_overflowStyle: {
x: 'hidden',
y: 'hidden'
},
_hasOverflow: {
x: false,
@@ -2384,15 +2389,6 @@ const getInstance = target => targetInstanceMap.get(target);
const createOSEventListenerHub = initialEventListeners => createEventListenerHub(initialEventListeners);
const createOverflowChangedArgs = (overflowAmount, hasOverflow, overflowScroll) => ({
amount: {
x: overflowAmount.w,
y: overflowAmount.h
},
overflow: hasOverflow,
scrollableOverflow: assignDeep({}, overflowScroll)
});
const OverlayScrollbars = (target, options, eventListeners) => {
const {
_getDefaultOptions,
@@ -2430,35 +2426,25 @@ const OverlayScrollbars = (target, options, eventListeners) => {
updateScrollbars(changedOptions, force);
};
const removeEnvListener = addEnvListener(update.bind(0, {}, true));
structureState._addOnUpdatedListener((updateHints, changedOptions, force) => {
const {
_sizeChanged,
_directionChanged,
_heightIntrinsicChanged,
_overflowAmountChanged,
_overflowScrollChanged,
_overflowStyleChanged,
_contentMutation,
_hostMutation
} = updateHints;
const {
_overflowAmount,
_overflowScroll,
_hasOverflow
} = structureState();
if (_overflowAmountChanged || _overflowScrollChanged) {
triggerEvent('overflowChanged', assignDeep({}, createOverflowChangedArgs(_overflowAmount, _hasOverflow, _overflowScroll), {
previous: createOverflowChangedArgs(_overflowAmount, _hasOverflow, _overflowScroll)
}));
}
triggerEvent('updated', {
updateHints: {
sizeChanged: _sizeChanged,
directionChanged: _directionChanged,
heightIntrinsicChanged: _heightIntrinsicChanged,
overflowAmountChanged: _overflowAmountChanged,
overflowScrollChanged: _overflowScrollChanged,
overflowStyleChanged: _overflowStyleChanged,
contentMutation: _contentMutation,
hostMutation: _hostMutation
},
@@ -2467,7 +2453,6 @@ const OverlayScrollbars = (target, options, eventListeners) => {
});
});
const removeEnvListener = addEnvListener(update.bind(0, {}, true));
const instance = {
options(newOptions) {
if (newOptions) {
@@ -2479,14 +2464,27 @@ const OverlayScrollbars = (target, options, eventListeners) => {
}
}
return currentOptions;
return assignDeep({}, currentOptions);
},
on: addEvent,
off: removeEvent,
state: () => ({
_overflowAmount: structureState()._overflowAmount
}),
state: () => {
const {
_overflowAmount,
_overflowStyle,
_hasOverflow,
_padding,
_paddingAbsolute
} = structureState();
return assignDeep({}, {
overflowAmount: _overflowAmount,
overflowStyle: _overflowStyle,
hasOverflow: _hasOverflow,
padding: _padding,
paddingAbsolute: _paddingAbsolute
});
},
update(force) {
update({}, force);
@@ -2513,7 +2511,38 @@ const OverlayScrollbars = (target, options, eventListeners) => {
triggerEvent('initialized');
return instance;
};
OverlayScrollbars.extend = addPlugin;
OverlayScrollbars.plugin = addPlugin;
OverlayScrollbars.env = () => {
const {
_nativeScrollbarSize,
_nativeScrollbarIsOverlaid,
_nativeScrollbarStyling,
_rtlScrollBehavior,
_flexboxGlue,
_cssCustomProperties,
_defaultInitializationStrategy,
_defaultDefaultOptions,
_getInitializationStrategy,
_setInitializationStrategy,
_getDefaultOptions,
_setDefaultOptions
} = getEnvironment();
return assignDeep({}, {
scrollbarSize: _nativeScrollbarSize,
scrollbarIsOverlaid: _nativeScrollbarIsOverlaid,
scrollbarStyling: _nativeScrollbarStyling,
rtlScrollBehavior: _rtlScrollBehavior,
flexboxGlue: _flexboxGlue,
cssCustomProperties: _cssCustomProperties,
defaultInitializationStrategy: _defaultInitializationStrategy,
defaultDefaultOptions: _defaultDefaultOptions,
getInitializationStrategy: _getInitializationStrategy,
setInitializationStrategy: _setInitializationStrategy,
getDefaultOptions: _getDefaultOptions,
setDefaultOptions: _setDefaultOptions
});
};
export { OverlayScrollbars as default };
//# sourceMappingURL=overlayscrollbars.esm.js.map
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+163 -121
View File
@@ -951,7 +951,10 @@
var envDOM = createDOM("<div class=\"" + classNameEnvironment + "\"><div></div></div>");
var envElm = envDOM[0];
var envChildElm = envElm.firstChild;
var onChangedListener = new Set();
var _createEventListenerH = createEventListenerHub(),
addEvent = _createEventListenerH[0],
triggerEvent = _createEventListenerH[2];
var _createCache = createCache({
_initialValue: getNativeScrollbarSize(body, envElm, envChildElm),
@@ -978,10 +981,7 @@
_rtlScrollBehavior: getRtlScrollBehavior(envElm, envChildElm),
_flexboxGlue: getFlexboxGlue(envElm, envChildElm),
_addListener: function _addListener(listener) {
onChangedListener.add(listener);
return function () {
return onChangedListener.delete(listener);
};
return addEvent('_', listener);
},
_getInitializationStrategy: assignDeep.bind(0, {}, initializationStrategy),
_setInitializationStrategy: function _setInitializationStrategy(newInitializationStrategy) {
@@ -1001,43 +1001,41 @@
var size = windowSize();
var dpr = getWindowDPR();
window.addEventListener('resize', function () {
if (onChangedListener.size) {
var sizeNew = windowSize();
var deltaSize = {
w: sizeNew.w - size.w,
h: sizeNew.h - size.h
};
if (deltaSize.w === 0 && deltaSize.h === 0) return;
var deltaAbsSize = {
w: abs(deltaSize.w),
h: abs(deltaSize.h)
};
var deltaAbsRatio = {
w: abs(round(sizeNew.w / (size.w / 100.0))),
h: abs(round(sizeNew.h / (size.h / 100.0)))
};
var dprNew = getWindowDPR();
var deltaIsBigger = deltaAbsSize.w > 2 && deltaAbsSize.h > 2;
var difference = !diffBiggerThanOne(deltaAbsRatio.w, deltaAbsRatio.h);
var dprChanged = dprNew !== dpr && dpr > 0;
var isZoom = deltaIsBigger && difference && dprChanged;
var sizeNew = windowSize();
var deltaSize = {
w: sizeNew.w - size.w,
h: sizeNew.h - size.h
};
if (deltaSize.w === 0 && deltaSize.h === 0) return;
var deltaAbsSize = {
w: abs(deltaSize.w),
h: abs(deltaSize.h)
};
var deltaAbsRatio = {
w: abs(round(sizeNew.w / (size.w / 100.0))),
h: abs(round(sizeNew.h / (size.h / 100.0)))
};
var dprNew = getWindowDPR();
var deltaIsBigger = deltaAbsSize.w > 2 && deltaAbsSize.h > 2;
var difference = !diffBiggerThanOne(deltaAbsRatio.w, deltaAbsRatio.h);
var dprChanged = dprNew !== dpr && dpr > 0;
var isZoom = deltaIsBigger && difference && dprChanged;
if (isZoom) {
var _updateNativeScrollba = updateNativeScrollbarSizeCache(getNativeScrollbarSize(body, envElm, envChildElm)),
scrollbarSize = _updateNativeScrollba[0],
scrollbarSizeChanged = _updateNativeScrollba[1];
if (isZoom) {
var _updateNativeScrollba = updateNativeScrollbarSizeCache(getNativeScrollbarSize(body, envElm, envChildElm)),
scrollbarSize = _updateNativeScrollba[0],
scrollbarSizeChanged = _updateNativeScrollba[1];
assignDeep(environmentInstance._nativeScrollbarSize, scrollbarSize);
removeElements(envElm);
assignDeep(environmentInstance._nativeScrollbarSize, scrollbarSize);
removeElements(envElm);
if (scrollbarSizeChanged) {
runEach(onChangedListener);
}
if (scrollbarSizeChanged) {
triggerEvent('_');
}
size = sizeNew;
dpr = dprNew;
}
size = sizeNew;
dpr = dprNew;
});
}
@@ -1205,12 +1203,16 @@
var _content = structureSetupElements._content;
var getState = state[0];
return function (updateHints) {
var _getEnvironment = getEnvironment(),
_flexboxGlue = _getEnvironment._flexboxGlue;
var _getState = getState(),
_heightIntrinsic = _getState._heightIntrinsic;
var _heightIntrinsicChanged = updateHints._heightIntrinsicChanged;
var heightIntrinsicChanged = (_content || !_flexboxGlue) && _heightIntrinsicChanged;
if (_heightIntrinsicChanged) {
if (heightIntrinsicChanged) {
style(_content, {
height: _heightIntrinsic ? '' : '100%',
display: _heightIntrinsic ? '' : 'inline'
@@ -1218,8 +1220,8 @@
}
return {
_sizeChanged: _heightIntrinsicChanged,
_contentMutation: _heightIntrinsicChanged
_sizeChanged: heightIntrinsicChanged,
_contentMutation: heightIntrinsicChanged
};
};
};
@@ -1305,6 +1307,7 @@
var max = Math.max;
var strVisible = 'visible';
var strHidden = 'hidden';
var overlaidScrollbarsHideOffset = 42;
var whCacheOptions = {
_equal: equalWH,
@@ -1316,8 +1319,8 @@
var xyCacheOptions = {
_equal: equalXY,
_initialValue: {
x: false,
y: false
x: strHidden,
y: strHidden
}
};
@@ -1370,7 +1373,7 @@
getCurrentOverflowAmountCache = _createCache3[1];
var _createCache4 = createCache(xyCacheOptions),
updateOverflowScrollCache = _createCache4[0];
updateOverflowStyleCache = _createCache4[0];
var fixFlexboxGlue = function fixFlexboxGlue(viewportOverflowState, heightIntrinsic) {
style(_viewport, {
@@ -1396,37 +1399,55 @@
};
var getViewportOverflowState = function getViewportOverflowState(showNativeOverlaidScrollbars, viewportStyleObj) {
var overlaidX = _nativeScrollbarIsOverlaid.x,
overlaidY = _nativeScrollbarIsOverlaid.y;
var determineOverflow = !viewportStyleObj;
var arrangeHideOffset = !_nativeScrollbarStyling && !showNativeOverlaidScrollbars ? overlaidScrollbarsHideOffset : 0;
var styleObj = determineOverflow ? style(_viewport, ['overflowX', 'overflowY']) : viewportStyleObj;
var scroll = {
x: styleObj.overflowX === 'scroll',
y: styleObj.overflowY === 'scroll'
};
var nonScrollbarStylingHideOffset = {
x: overlaidX ? arrangeHideOffset : _nativeScrollbarSize.x,
y: overlaidY ? arrangeHideOffset : _nativeScrollbarSize.y
};
var scrollbarsHideOffset = {
x: scroll.x && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset.x : 0,
y: scroll.y && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset.y : 0
var getStatePerAxis = function getStatePerAxis(styleKey, isOverlaid, nativeScrollbarSize) {
var overflowStyle = style(_viewport, styleKey);
var objectPrefferedOverflowStyle = viewportStyleObj ? viewportStyleObj[styleKey] : overflowStyle;
var overflowScroll = objectPrefferedOverflowStyle === 'scroll';
var nonScrollbarStylingHideOffset = isOverlaid ? arrangeHideOffset : nativeScrollbarSize;
var scrollbarsHideOffset = overflowScroll && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset : 0;
var scrollbarsHideOffsetArrange = isOverlaid && !!arrangeHideOffset;
return [overflowStyle, overflowScroll, scrollbarsHideOffset, scrollbarsHideOffsetArrange];
};
var _getStatePerAxis = getStatePerAxis('overflowX', _nativeScrollbarIsOverlaid.x, _nativeScrollbarSize.x),
xOverflowStyle = _getStatePerAxis[0],
xOverflowScroll = _getStatePerAxis[1],
xScrollbarsHideOffset = _getStatePerAxis[2],
xScrollbarsHideOffsetArrange = _getStatePerAxis[3];
var _getStatePerAxis2 = getStatePerAxis('overflowY', _nativeScrollbarIsOverlaid.y, _nativeScrollbarSize.y),
yOverflowStyle = _getStatePerAxis2[0],
yOverflowScroll = _getStatePerAxis2[1],
yScrollbarsHideOffset = _getStatePerAxis2[2],
yScrollbarsHideOffsetArrange = _getStatePerAxis2[3];
return {
_overflowScroll: scroll,
_scrollbarsHideOffsetArrange: {
x: overlaidX && !!arrangeHideOffset,
y: overlaidY && !!arrangeHideOffset
_overflowStyle: {
x: xOverflowStyle,
y: yOverflowStyle
},
_scrollbarsHideOffset: scrollbarsHideOffset
_overflowScroll: {
x: xOverflowScroll,
y: yOverflowScroll
},
_scrollbarsHideOffset: {
x: xScrollbarsHideOffset,
y: yScrollbarsHideOffset
},
_scrollbarsHideOffsetArrange: {
x: xScrollbarsHideOffsetArrange,
y: yScrollbarsHideOffsetArrange
}
};
};
var setViewportOverflowState = function setViewportOverflowState(showNativeOverlaidScrollbars, hasOverflow, overflowOption, viewportStyleObj) {
var setAxisOverflowStyle = function setAxisOverflowStyle(behavior, hasOverflowAxis) {
var overflowVisible = overflowIsVisible(behavior);
return [hasOverflowAxis && !overflowVisible ? behavior : '', hasOverflowAxis && overflowVisible && behavior.replace(strVisible + "-", '') || ''];
var overflowVisibleBehavior = hasOverflowAxis && overflowVisible && behavior.replace(strVisible + "-", '') || '';
return [hasOverflowAxis && !overflowVisible ? behavior : '', overflowIsVisible(overflowVisibleBehavior) ? 'hidden' : overflowVisibleBehavior];
};
var _setAxisOverflowStyle = setAxisOverflowStyle(overflowOption.x, hasOverflow.x),
@@ -1589,7 +1610,6 @@
var sizeFractionCache = getCurrentSizeFraction(force);
var viewportScrollSizeCache = getCurrentViewportScrollSizeCache(force);
var overflowAmuntCache = getCurrentOverflowAmountCache(force);
var updateHintsReturn;
var preMeasureViewportOverflowState;
if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarStyling) {
@@ -1601,11 +1621,11 @@
fixFlexboxGlue(preMeasureViewportOverflowState, _heightIntrinsic);
}
if (overflowVisible) {
removeClass(_viewport, classNameOverflowVisible);
}
if (_sizeChanged || _paddingStyleChanged || _contentMutation || _directionChanged || showNativeOverlaidScrollbarsChanged) {
if (overflowVisible) {
removeClass(_viewport, classNameOverflowVisible);
}
var _undoViewportArrange = undoViewportArrange(showNativeOverlaidScrollbars, _directionIsRTL, preMeasureViewportOverflowState),
redoViewportArrange = _undoViewportArrange[0],
undoViewportArrangeOverflowState = _undoViewportArrange[1];
@@ -1663,11 +1683,6 @@
};
var viewportOverflowState = setViewportOverflowState(showNativeOverlaidScrollbars, hasOverflow, overflow, viewportStyle);
var viewportArranged = arrangeViewport(viewportOverflowState, viewportScrollSize, sizeFraction, _directionIsRTL);
var _updateOverflowScroll = updateOverflowScrollCache(viewportOverflowState._overflowScroll),
overflowScroll = _updateOverflowScroll[0],
overflowScrollChanged = _updateOverflowScroll[1];
hideNativeScrollbars(viewportOverflowState, _directionIsRTL, viewportArranged, viewportStyle);
if (adjustFlexboxGlue) {
@@ -1675,21 +1690,28 @@
}
style(_viewport, viewportStyle);
setState({
_overflowScroll: overflowScroll,
_overflowAmount: overflowAmount,
_hasOverflow: hasOverflow
});
updateHintsReturn = {
_overflowAmountChanged: overflowAmountChanged,
_overflowScrollChanged: overflowScrollChanged
};
}
attr(_host, dataAttributeHost, removeClipping ? 'overflowVisible' : '');
conditionalClass(_padding, classNameOverflowVisible, removeClipping);
conditionalClass(_viewport, classNameOverflowVisible, overflowVisible);
return updateHintsReturn;
var _updateOverflowStyleC = updateOverflowStyleCache(getViewportOverflowState(showNativeOverlaidScrollbars)._overflowStyle),
overflowStyle = _updateOverflowStyleC[0],
overflowStyleChanged = _updateOverflowStyleC[1];
setState({
_overflowStyle: overflowStyle,
_overflowAmount: {
x: overflowAmount.w,
y: overflowAmount.h
},
_hasOverflow: hasOverflow
});
return {
_overflowStyleChanged: overflowStyleChanged,
_overflowAmountChanged: overflowAmountChanged
};
};
};
@@ -1721,8 +1743,8 @@
_paddingStyleChanged: false,
_directionChanged: false,
_heightIntrinsicChanged: false,
_overflowScrollChanged: false,
_overflowAmountChanged: false,
_overflowStyleChanged: false,
_hostMutation: false,
_contentMutation: false
}, updateHints), {}, force);
@@ -2340,12 +2362,12 @@
paddingLeft: 0
},
_overflowAmount: {
w: 0,
h: 0
x: 0,
y: 0
},
_overflowScroll: {
x: false,
y: false
_overflowStyle: {
x: 'hidden',
y: 'hidden'
},
_hasOverflow: {
x: false,
@@ -2557,17 +2579,6 @@
return createEventListenerHub(initialEventListeners);
};
var createOverflowChangedArgs = function createOverflowChangedArgs(overflowAmount, hasOverflow, overflowScroll) {
return {
amount: {
x: overflowAmount.w,
y: overflowAmount.h
},
overflow: hasOverflow,
scrollableOverflow: assignDeep({}, overflowScroll)
};
};
var OverlayScrollbars = function OverlayScrollbars(target, options, eventListeners) {
var _getEnvironment = getEnvironment(),
_getDefaultOptions = _getEnvironment._getDefaultOptions,
@@ -2615,33 +2626,23 @@
updateScrollbars(changedOptions, force);
};
var removeEnvListener = addEnvListener(_update.bind(0, {}, true));
structureState._addOnUpdatedListener(function (updateHints, changedOptions, force) {
var _sizeChanged = updateHints._sizeChanged,
_directionChanged = updateHints._directionChanged,
_heightIntrinsicChanged = updateHints._heightIntrinsicChanged,
_overflowAmountChanged = updateHints._overflowAmountChanged,
_overflowScrollChanged = updateHints._overflowScrollChanged,
_overflowStyleChanged = updateHints._overflowStyleChanged,
_contentMutation = updateHints._contentMutation,
_hostMutation = updateHints._hostMutation;
var _structureState = structureState(),
_overflowAmount = _structureState._overflowAmount,
_overflowScroll = _structureState._overflowScroll,
_hasOverflow = _structureState._hasOverflow;
if (_overflowAmountChanged || _overflowScrollChanged) {
triggerEvent('overflowChanged', assignDeep({}, createOverflowChangedArgs(_overflowAmount, _hasOverflow, _overflowScroll), {
previous: createOverflowChangedArgs(_overflowAmount, _hasOverflow, _overflowScroll)
}));
}
triggerEvent('updated', {
updateHints: {
sizeChanged: _sizeChanged,
directionChanged: _directionChanged,
heightIntrinsicChanged: _heightIntrinsicChanged,
overflowAmountChanged: _overflowAmountChanged,
overflowScrollChanged: _overflowScrollChanged,
overflowStyleChanged: _overflowStyleChanged,
contentMutation: _contentMutation,
hostMutation: _hostMutation
},
@@ -2650,7 +2651,6 @@
});
});
var removeEnvListener = addEnvListener(_update.bind(0, {}, true));
var instance = {
options: function options(newOptions) {
if (newOptions) {
@@ -2663,14 +2663,25 @@
}
}
return currentOptions;
return assignDeep({}, currentOptions);
},
on: addEvent,
off: removeEvent,
state: function state() {
return {
_overflowAmount: structureState()._overflowAmount
};
var _structureState = structureState(),
_overflowAmount = _structureState._overflowAmount,
_overflowStyle = _structureState._overflowStyle,
_hasOverflow = _structureState._hasOverflow,
_padding = _structureState._padding,
_paddingAbsolute = _structureState._paddingAbsolute;
return assignDeep({}, {
overflowAmount: _overflowAmount,
overflowStyle: _overflowStyle,
hasOverflow: _hasOverflow,
padding: _padding,
paddingAbsolute: _paddingAbsolute
});
},
update: function update(force) {
_update({}, force);
@@ -2696,7 +2707,38 @@
triggerEvent('initialized');
return instance;
};
OverlayScrollbars.extend = addPlugin;
OverlayScrollbars.plugin = addPlugin;
OverlayScrollbars.env = function () {
var _getEnvironment2 = getEnvironment(),
_nativeScrollbarSize = _getEnvironment2._nativeScrollbarSize,
_nativeScrollbarIsOverlaid = _getEnvironment2._nativeScrollbarIsOverlaid,
_nativeScrollbarStyling = _getEnvironment2._nativeScrollbarStyling,
_rtlScrollBehavior = _getEnvironment2._rtlScrollBehavior,
_flexboxGlue = _getEnvironment2._flexboxGlue,
_cssCustomProperties = _getEnvironment2._cssCustomProperties,
_defaultInitializationStrategy = _getEnvironment2._defaultInitializationStrategy,
_defaultDefaultOptions = _getEnvironment2._defaultDefaultOptions,
_getInitializationStrategy = _getEnvironment2._getInitializationStrategy,
_setInitializationStrategy = _getEnvironment2._setInitializationStrategy,
_getDefaultOptions = _getEnvironment2._getDefaultOptions,
_setDefaultOptions = _getEnvironment2._setDefaultOptions;
return assignDeep({}, {
scrollbarSize: _nativeScrollbarSize,
scrollbarIsOverlaid: _nativeScrollbarIsOverlaid,
scrollbarStyling: _nativeScrollbarStyling,
rtlScrollBehavior: _rtlScrollBehavior,
flexboxGlue: _flexboxGlue,
cssCustomProperties: _cssCustomProperties,
defaultInitializationStrategy: _defaultInitializationStrategy,
defaultDefaultOptions: _defaultDefaultOptions,
getInitializationStrategy: _getInitializationStrategy,
setInitializationStrategy: _setInitializationStrategy,
getDefaultOptions: _getDefaultOptions,
setDefaultOptions: _setDefaultOptions
});
};
return OverlayScrollbars;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+39 -40
View File
@@ -12,13 +12,14 @@ import {
removeAttr,
removeElements,
windowSize,
runEach,
equalBCRWH,
getBoundingClientRect,
assignDeep,
cssProperty,
createCache,
equalXY,
createEventListenerHub,
EventListener,
} from 'support';
import {
classNameEnvironment,
@@ -80,7 +81,10 @@ export type DefaultInitializationStrategy = {
>;
};
export type OnEnvironmentChanged = (env: Environment) => void;
export interface EnvironmentListenersNameArgsMap {
_: undefined;
}
export interface Environment {
readonly _nativeScrollbarSize: XY;
readonly _nativeScrollbarIsOverlaid: XY<boolean>;
@@ -90,7 +94,7 @@ export interface Environment {
readonly _cssCustomProperties: boolean;
readonly _defaultInitializationStrategy: DefaultInitializationStrategy;
readonly _defaultDefaultOptions: OSOptions;
_addListener(listener: OnEnvironmentChanged): () => void;
_addListener(listener: EventListener<EnvironmentListenersNameArgsMap, '_'>): () => void;
_getInitializationStrategy(): InitializationStrategy;
_setInitializationStrategy(newInitializationStrategy: Partial<InitializationStrategy>): void;
_getDefaultOptions(): OSOptions;
@@ -208,7 +212,7 @@ const createEnvironment = (): Environment => {
const envDOM = createDOM(`<div class="${classNameEnvironment}"><div></div></div>`);
const envElm = envDOM[0] as HTMLElement;
const envChildElm = envElm.firstChild as HTMLElement;
const onChangedListener: Set<OnEnvironmentChanged> = new Set();
const [addEvent, , triggerEvent] = createEventListenerHub<EnvironmentListenersNameArgsMap>();
const [updateNativeScrollbarSizeCache, getNativeScrollbarSizeCache] = createCache({
_initialValue: getNativeScrollbarSize(body, envElm, envChildElm),
_equal: equalXY,
@@ -229,10 +233,7 @@ const createEnvironment = (): Environment => {
_cssCustomProperties: style(envElm, 'zIndex') === '-1',
_rtlScrollBehavior: getRtlScrollBehavior(envElm, envChildElm),
_flexboxGlue: getFlexboxGlue(envElm, envChildElm),
_addListener(listener) {
onChangedListener.add(listener);
return () => onChangedListener.delete(listener);
},
_addListener: (listener) => addEvent('_', listener),
_getInitializationStrategy: assignDeep<InitializationStrategy, InitializationStrategy>.bind(
0,
{} as InitializationStrategy,
@@ -261,45 +262,43 @@ const createEnvironment = (): Environment => {
let dpr = getWindowDPR();
window.addEventListener('resize', () => {
if (onChangedListener.size) {
const sizeNew = windowSize();
const deltaSize = {
w: sizeNew.w - size.w,
h: sizeNew.h - size.h,
};
const sizeNew = windowSize();
const deltaSize = {
w: sizeNew.w - size.w,
h: sizeNew.h - size.h,
};
if (deltaSize.w === 0 && deltaSize.h === 0) return;
if (deltaSize.w === 0 && deltaSize.h === 0) return;
const deltaAbsSize = {
w: abs(deltaSize.w),
h: abs(deltaSize.h),
};
const deltaAbsRatio = {
w: abs(round(sizeNew.w / (size.w / 100.0))),
h: abs(round(sizeNew.h / (size.h / 100.0))),
};
const dprNew = getWindowDPR();
const deltaIsBigger = deltaAbsSize.w > 2 && deltaAbsSize.h > 2;
const difference = !diffBiggerThanOne(deltaAbsRatio.w, deltaAbsRatio.h);
const dprChanged = dprNew !== dpr && dpr > 0;
const isZoom = deltaIsBigger && difference && dprChanged;
const deltaAbsSize = {
w: abs(deltaSize.w),
h: abs(deltaSize.h),
};
const deltaAbsRatio = {
w: abs(round(sizeNew.w / (size.w / 100.0))),
h: abs(round(sizeNew.h / (size.h / 100.0))),
};
const dprNew = getWindowDPR();
const deltaIsBigger = deltaAbsSize.w > 2 && deltaAbsSize.h > 2;
const difference = !diffBiggerThanOne(deltaAbsRatio.w, deltaAbsRatio.h);
const dprChanged = dprNew !== dpr && dpr > 0;
const isZoom = deltaIsBigger && difference && dprChanged;
if (isZoom) {
const [scrollbarSize, scrollbarSizeChanged] = updateNativeScrollbarSizeCache(
getNativeScrollbarSize(body, envElm, envChildElm)
);
if (isZoom) {
const [scrollbarSize, scrollbarSizeChanged] = updateNativeScrollbarSizeCache(
getNativeScrollbarSize(body, envElm, envChildElm)
);
assignDeep(environmentInstance._nativeScrollbarSize, scrollbarSize); // keep the object same!
removeElements(envElm);
assignDeep(environmentInstance._nativeScrollbarSize, scrollbarSize); // keep the object same!
removeElements(envElm);
if (scrollbarSizeChanged) {
runEach(onChangedListener);
}
if (scrollbarSizeChanged) {
triggerEvent('_');
}
size = sizeNew;
dpr = dprNew;
}
size = sizeNew;
dpr = dprNew;
});
}
@@ -1,5 +1,5 @@
import { OSOptions } from 'options';
import { createEventListenerHub, XY } from 'support';
import { createEventListenerHub } from 'support';
import { PartialOptions } from 'typings';
import type {
InitialEventListeners,
@@ -25,7 +25,7 @@ export interface OnUpdatedEventListenerArgs {
directionChanged: boolean;
heightIntrinsicChanged: boolean;
overflowAmountChanged: boolean;
overflowScrollChanged: boolean;
overflowStyleChanged: boolean;
hostMutation: boolean;
contentMutation: boolean;
};
@@ -33,21 +33,9 @@ export interface OnUpdatedEventListenerArgs {
force: boolean;
}
export interface OnOverflowChangedEventListenerArgs {
overflow: XY<boolean>; // whether there is an overflow
scrollableOverflow: XY<boolean>; // whether there is an scrollable overflow
amount: XY<number>; // the overflow amount in pixel
previous: {
overflow: XY<boolean>;
scrollableOverflow: XY<boolean>;
amount: XY<number>;
};
}
export interface OSEventListenersNameArgsMap {
initialized: undefined;
initializationWithdrawn: undefined;
overflowChanged: OnOverflowChangedEventListenerArgs;
updated: OnUpdatedEventListenerArgs;
destroyed: undefined;
}
+7 -33
View File
@@ -39,7 +39,6 @@ export type SizeChangedCallback = (this: any, args?: SizeChangedArgs) => void;
export type UpdatedCallback = (this: any, args?: UpdatedArgs) => void;
export interface OSOptions {
resize: ResizeBehavior;
paddingAbsolute: boolean;
updating: {
elementEvents: Array<[string, string]> | null;
@@ -58,34 +57,10 @@ export interface OSOptions {
clickScroll: boolean;
touch: boolean;
};
textarea: {
dynWidth: boolean;
dynHeight: boolean;
inheritedAttrs: string | Array<string> | null;
};
nativeScrollbarsOverlaid: {
show: boolean;
initialize: boolean;
};
callbacks: {
onUpdated: (() => any) | null;
};
/*
callbacks?: {
onInitialized?: BasicEventCallback | null;
onInitializationWithdrawn?: BasicEventCallback | null;
onDestroyed?: BasicEventCallback | null;
onScrollStart?: ScrollEventCallback | null;
onScroll?: ScrollEventCallback | null;
onScrollStop?: ScrollEventCallback | null;
onOverflowChanged?: OverflowChangedCallback | null;
onOverflowAmountChanged?: OverflowAmountChangedCallback | null;
onDirectionChanged?: DirectionChangedCallback | null;
onContentSizeChanged?: SizeChangedCallback | null;
onHostSizeChanged?: SizeChangedCallback | null;
onUpdated?: UpdatedCallback | null;
};
*/
}
export type ReadonlyOSOptions = ReadonlyOptions<OSOptions>;
@@ -118,7 +93,7 @@ export interface UpdatedArgs {
}
export const defaultOptions: OSOptions = {
resize: 'none', // none || both || horizontal || vertical || n || b || h || v
// resize: 'none', // none || both || horizontal || vertical || n || b || h || v
paddingAbsolute: false, // true || false
updating: {
elementEvents: [['img', 'load']], // array of tuples || null
@@ -129,6 +104,10 @@ export const defaultOptions: OSOptions = {
x: 'scroll', // visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s
y: 'scroll', // visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s
},
nativeScrollbarsOverlaid: {
show: false, // true || false
initialize: false, // true || false
},
scrollbars: {
visibility: 'auto', // visible || hidden || auto || v || h || a
autoHide: 'never', // never || scroll || leave || move || n || s || l || m
@@ -137,18 +116,13 @@ export const defaultOptions: OSOptions = {
clickScroll: false, // true || false
touch: true, // true || false
},
/*
textarea: {
dynWidth: false, // true || false
dynHeight: false, // true || false
inheritedAttrs: ['style', 'class'], // string || array || null
},
nativeScrollbarsOverlaid: {
show: false, // true || false
initialize: false, // true || false
},
callbacks: {
onUpdated: null,
},
*/
};
export const getOptionsDiff = <T>(
@@ -1,8 +1,17 @@
import { OSTarget, OSInitializationObject, PartialOptions } from 'typings';
import { assignDeep, isEmptyObject, each, isFunction, keys, isHTMLElement, WH, XY } from 'support';
import { OSTarget, OSInitializationObject, PartialOptions, OverflowStyle } from 'typings';
import {
assignDeep,
isEmptyObject,
each,
isFunction,
keys,
isHTMLElement,
XY,
TRBL,
} from 'support';
import { createStructureSetup, createScrollbarsSetup } from 'setups';
import { getOptionsDiff, OSOptions, ReadonlyOSOptions } from 'options';
import { getEnvironment } from 'environment';
import { DefaultInitializationStrategy, getEnvironment, InitializationStrategy } from 'environment';
import {
getPlugins,
addPlugin,
@@ -25,7 +34,32 @@ export interface OverlayScrollbarsStatic {
eventListeners?: InitialOSEventListeners
): OverlayScrollbars;
extend(osPlugin: OSPlugin | OSPlugin[]): void;
plugin(osPlugin: OSPlugin | OSPlugin[]): void;
env(): OverlayScrollbarsEnv;
}
export interface OverlayScrollbarsEnv {
scrollbarSize: XY<number>;
scrollbarIsOverlaid: XY<boolean>;
scrollbarStyling: boolean;
rtlScrollBehavior: { n: boolean; i: boolean };
flexboxGlue: boolean;
cssCustomProperties: boolean;
defaultInitializationStrategy: DefaultInitializationStrategy;
defaultDefaultOptions: OSOptions;
getInitializationStrategy(): InitializationStrategy;
setInitializationStrategy(newInitializationStrategy: Partial<InitializationStrategy>): void;
getDefaultOptions(): OSOptions;
setDefaultOptions(newDefaultOptions: PartialOptions<OSOptions>): void;
}
export interface OverlayScrollbarsState {
padding: TRBL;
paddingAbsolute: boolean;
overflowAmount: XY<number>;
overflowStyle: XY<OverflowStyle>;
hasOverflow: XY<boolean>;
}
export interface OverlayScrollbars {
@@ -35,24 +69,16 @@ export interface OverlayScrollbars {
update(force?: boolean): void;
destroy(): void;
state(): any;
state(): OverlayScrollbarsState;
on: AddOSEventListener;
off: RemoveOSEventListener;
}
const createOverflowChangedArgs = (
overflowAmount: WH<number>,
hasOverflow: XY<boolean>,
overflowScroll: XY<boolean>
) => ({
amount: {
x: overflowAmount.w,
y: overflowAmount.h,
},
overflow: hasOverflow,
scrollableOverflow: assignDeep({}, overflowScroll),
});
/**
* Notes:
* Height intrinsic detection use "content: true" init strategy - or open ticket for custom height intrinsic observer
*/
export const OverlayScrollbars: OverlayScrollbarsStatic = (
target,
@@ -109,26 +135,18 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
updateScrollbars(changedOptions, force);
};
const removeEnvListener = addEnvListener(update.bind(0, {}, true));
structureState._addOnUpdatedListener((updateHints, changedOptions, force) => {
const {
_sizeChanged,
_directionChanged,
_heightIntrinsicChanged,
_overflowAmountChanged,
_overflowScrollChanged,
_overflowStyleChanged,
_contentMutation,
_hostMutation,
} = updateHints;
const { _overflowAmount, _overflowScroll, _hasOverflow } = structureState();
if (_overflowAmountChanged || _overflowScrollChanged) {
triggerEvent(
'overflowChanged',
assignDeep({}, createOverflowChangedArgs(_overflowAmount, _hasOverflow, _overflowScroll), {
previous: createOverflowChangedArgs(_overflowAmount!, _hasOverflow, _overflowScroll!),
})
);
}
triggerEvent('updated', {
updateHints: {
@@ -136,7 +154,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
directionChanged: _directionChanged,
heightIntrinsicChanged: _heightIntrinsicChanged,
overflowAmountChanged: _overflowAmountChanged,
overflowScrollChanged: _overflowScrollChanged,
overflowStyleChanged: _overflowStyleChanged,
contentMutation: _contentMutation,
hostMutation: _hostMutation,
},
@@ -145,8 +163,6 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
});
});
const removeEnvListener = addEnvListener(update.bind(0, {}, true));
const instance: OverlayScrollbars = {
options(newOptions?: PartialOptions<OSOptions>) {
if (newOptions) {
@@ -157,13 +173,24 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
update(changedOptions);
}
}
return currentOptions;
return assignDeep({}, currentOptions);
},
on: addEvent,
off: removeEvent,
state: () => ({
_overflowAmount: structureState()._overflowAmount,
}),
state: () => {
const { _overflowAmount, _overflowStyle, _hasOverflow, _padding, _paddingAbsolute } =
structureState();
return assignDeep(
{},
{
overflowAmount: _overflowAmount,
overflowStyle: _overflowStyle,
hasOverflow: _hasOverflow,
padding: _padding,
paddingAbsolute: _paddingAbsolute,
}
);
},
update(force?: boolean) {
update({}, force);
},
@@ -195,4 +222,38 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
return instance;
};
OverlayScrollbars.extend = addPlugin;
OverlayScrollbars.plugin = addPlugin;
OverlayScrollbars.env = () => {
const {
_nativeScrollbarSize,
_nativeScrollbarIsOverlaid,
_nativeScrollbarStyling,
_rtlScrollBehavior,
_flexboxGlue,
_cssCustomProperties,
_defaultInitializationStrategy,
_defaultDefaultOptions,
_getInitializationStrategy,
_setInitializationStrategy,
_getDefaultOptions,
_setDefaultOptions,
} = getEnvironment();
return assignDeep(
{},
{
scrollbarSize: _nativeScrollbarSize,
scrollbarIsOverlaid: _nativeScrollbarIsOverlaid,
scrollbarStyling: _nativeScrollbarStyling,
rtlScrollBehavior: _rtlScrollBehavior,
flexboxGlue: _flexboxGlue,
cssCustomProperties: _cssCustomProperties,
defaultInitializationStrategy: _defaultInitializationStrategy,
defaultDefaultOptions: _defaultDefaultOptions,
getInitializationStrategy: _getInitializationStrategy,
setInitializationStrategy: _setInitializationStrategy,
getDefaultOptions: _getDefaultOptions,
setDefaultOptions: _setDefaultOptions,
}
);
};
@@ -1,11 +1,5 @@
import { OSPlugin } from 'plugins';
import {
OSOptions,
ResizeBehavior,
OverflowBehavior,
VisibilityBehavior,
AutoHideBehavior,
} from 'options';
import { OSOptions, OverflowBehavior, VisibilityBehavior, AutoHideBehavior } from 'options';
import {
validateOptions,
OptionsTemplate,
@@ -17,12 +11,6 @@ import { PartialOptions } from 'typings';
const numberAllowedValues: OptionsTemplateValue<number> = oTypes.number;
const booleanAllowedValues: OptionsTemplateValue<boolean> = oTypes.boolean;
const arrayNullValues: OptionsTemplateValue<Array<unknown> | null> = [oTypes.array, oTypes.null];
const stringArrayNullAllowedValues: OptionsTemplateValue<string | ReadonlyArray<string> | null> = [
oTypes.string,
oTypes.array,
oTypes.null,
];
const resizeAllowedValues: OptionsTemplateValue<ResizeBehavior> = 'none both horizontal vertical';
const overflowAllowedValues: OptionsTemplateValue<OverflowBehavior> =
'hidden scroll visible visible-hidden';
const scrollbarsVisibilityAllowedValues: OptionsTemplateValue<VisibilityBehavior> =
@@ -31,7 +19,7 @@ const scrollbarsAutoHideAllowedValues: OptionsTemplateValue<AutoHideBehavior> =
'never scroll leavemove';
const optionsTemplate: OptionsTemplate<OSOptions> = {
resize: resizeAllowedValues, // none || both || horizontal || vertical || n || b ||
// resize: resizeAllowedValues, // none || both || horizontal || vertical || n || b ||
paddingAbsolute: booleanAllowedValues, // true || false
updating: {
elementEvents: arrayNullValues, // array of tuples || null
@@ -50,18 +38,17 @@ const optionsTemplate: OptionsTemplate<OSOptions> = {
clickScroll: booleanAllowedValues, // true || false
touch: booleanAllowedValues, // true || false
},
/*
textarea: {
dynWidth: booleanAllowedValues, // true || false
dynHeight: booleanAllowedValues, // true || false
inheritedAttrs: stringArrayNullAllowedValues, // string || array || nul
},
*/
nativeScrollbarsOverlaid: {
show: booleanAllowedValues, // true || false
initialize: booleanAllowedValues, // true || false
},
callbacks: {
onUpdated: [oTypes.function, oTypes.null],
},
};
export type OptionsValidationPluginInstance = {
@@ -10,8 +10,23 @@ import {
removeAttr,
CacheValues,
keys,
liesBetween,
scrollSize,
equalWH,
createCache,
WH,
fractionalSize,
removeClass,
addClass,
hasClass,
} from 'support';
import { getEnvironment } from 'environment';
import {
dataAttributeHost,
classNameViewport,
classNameContent,
classNameOverflowVisible,
} from 'classnames';
import { createSizeObserver, SizeObserverCallbackParams } from 'observers/sizeObserver';
import { createTrinsicObserver } from 'observers/trinsicObserver';
import { createDOMObserver, DOMObserver } from 'observers/domObserver';
@@ -36,14 +51,14 @@ type ExcludeFromTuple<T extends readonly any[], E> = T extends [infer F, ...infe
: [F, ...ExcludeFromTuple<R, E>]
: [];
// const hostSelector = `.${classNameHost}`;
const hostSelector = `[${dataAttributeHost}]`;
// TODO: observer textarea attrs if textarea
// TODO: test _ignoreContentChange & _ignoreNestedTargetChange for content dom observer
// TODO: test _ignoreTargetChange for target dom observer
// const viewportSelector = `.${classNameViewport}`;
// const contentSelector = `.${classNameContent}`;
const viewportSelector = `.${classNameViewport}`;
const contentSelector = `.${classNameContent}`;
const ignorePrefix = 'os-';
const viewportAttrsFromTarget = ['tabindex'];
const baseStyleChangingAttrsTextarea = ['wrap', 'cols', 'rows'];
@@ -75,6 +90,25 @@ export const createStructureSetupObservers = (
const [, setState] = state;
const { _host, _viewport, _content, _isTextarea } = structureSetupElements;
const { _nativeScrollbarStyling, _flexboxGlue } = getEnvironment();
const [updateContentSizeCache] = createCache<WH<number>>(
{
_equal: equalWH,
_initialValue: { w: 0, h: 0 },
},
() => {
const has = hasClass(_viewport, classNameOverflowVisible);
has && removeClass(_viewport, classNameOverflowVisible);
const scroll = scrollSize(_viewport);
const fractional = fractionalSize(_viewport);
has && addClass(_viewport, classNameOverflowVisible);
return {
w: scroll.w + fractional.w,
h: scroll.h + fractional.h,
};
}
);
const contentMutationObserverAttr = _isTextarea
? baseStyleChangingAttrsTextarea
: baseStyleChangingAttrs.concat(baseStyleChangingAttrsTextarea);
@@ -132,13 +166,17 @@ export const createStructureSetupObservers = (
updateFn({ _sizeChanged, _directionChanged: directionChanged });
};
const onContentMutation = (contentChangedTroughEvent: boolean) => {
const [, contentSizeChanged] = updateContentSizeCache();
// if contentChangedTroughEvent is true its already debounced
const updateFn = contentChangedTroughEvent
? structureSetupUpdate
: structureSetupUpdateWithDebouncedAdaptiveUpdateHints;
updateFn({
_contentMutation: true,
});
if (contentSizeChanged) {
updateFn({
_contentMutation: true,
});
}
};
const onHostMutation = (targetChangedAttrs: string[], targetStyleChanged: boolean) => {
if (targetStyleChanged) {
@@ -189,17 +227,17 @@ export const createStructureSetupObservers = (
_attributes: contentMutationObserverAttr.concat(attributes || []),
_eventContentChange: elementEvents,
_ignoreNestedTargetChange: ignoreTargetChange,
// _nestedTargetSelector: hostSelector,
/*
_ignoreContentChange: (mutation, isNestedTarget) => {
const { target, attributeName } = mutation;
return isNestedTarget
? false
: attributeName
? liesBetween(target as Element, hostSelector, viewportSelector) || liesBetween(target as Element, hostSelector, contentSelector)
: false;
},
*/
_nestedTargetSelector: hostSelector,
_ignoreContentChange: (mutation, isNestedTarget) => {
const { target, attributeName } = mutation;
return !isNestedTarget && attributeName
? liesBetween(
target as Element,
hostSelector,
_content ? contentSelector : viewportSelector
)
: false;
},
}
);
}
@@ -5,17 +5,17 @@ import { createStructureSetupUpdate } from 'setups/structureSetup/structureSetup
import { createStructureSetupObservers } from 'setups/structureSetup/structureSetup.observers';
import type { StructureSetupUpdateHints } from 'setups/structureSetup/structureSetup.update';
import type { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements';
import type { TRBL, XY, WH } from 'support';
import type { TRBL, XY } from 'support';
import type { OSOptions, ReadonlyOSOptions } from 'options';
import type { Setup } from 'setups';
import type { OSTarget, PartialOptions, StyleObject } from 'typings';
import type { OSTarget, PartialOptions, StyleObject, OverflowStyle } from 'typings';
export interface StructureSetupState {
_padding: TRBL;
_paddingAbsolute: boolean;
_viewportPaddingStyle: StyleObject;
_overflowScroll: XY<boolean>;
_overflowAmount: WH<number>;
_overflowAmount: XY<number>;
_overflowStyle: XY<OverflowStyle>;
_hasOverflow: XY<boolean>;
_heightIntrinsic: boolean;
_directionIsRTL: boolean;
@@ -50,12 +50,12 @@ const initialStructureSetupUpdateState: StructureSetupState = {
paddingLeft: 0,
},
_overflowAmount: {
w: 0,
h: 0,
x: 0,
y: 0,
},
_overflowScroll: {
x: false,
y: false,
_overflowStyle: {
x: 'hidden',
y: 'hidden',
},
_hasOverflow: {
x: false,
@@ -26,8 +26,8 @@ export interface StructureSetupUpdateHints {
_sizeChanged: boolean;
_directionChanged: boolean;
_heightIntrinsicChanged: boolean;
_overflowScrollChanged: boolean;
_overflowAmountChanged: boolean;
_overflowStyleChanged: boolean;
_paddingStyleChanged: boolean;
_hostMutation: boolean;
_contentMutation: boolean;
@@ -78,8 +78,8 @@ export const createStructureSetupUpdate = (
_paddingStyleChanged: false,
_directionChanged: false,
_heightIntrinsicChanged: false,
_overflowScrollChanged: false,
_overflowAmountChanged: false,
_overflowStyleChanged: false,
_hostMutation: false,
_contentMutation: false,
},
@@ -22,7 +22,7 @@ import {
classNameOverflowVisible,
dataAttributeHost,
} from 'classnames';
import type { StyleObject } from 'typings';
import type { StyleObject, OverflowStyle } from 'typings';
import type { OverflowBehavior } from 'options';
import type { CreateStructureUpdateSegment } from 'setups/structureSetup/structureSetup.update';
@@ -30,6 +30,7 @@ interface ViewportOverflowState {
_scrollbarsHideOffset: XY<number>;
_scrollbarsHideOffsetArrange: XY<boolean>;
_overflowScroll: XY<boolean>;
_overflowStyle: XY<OverflowStyle>;
}
type UndoViewportArrangeResult = [
@@ -39,6 +40,7 @@ type UndoViewportArrangeResult = [
const { max } = Math;
const strVisible = 'visible';
const strHidden = 'hidden';
const overlaidScrollbarsHideOffset = 42;
const whCacheOptions = {
_equal: equalWH,
@@ -46,7 +48,7 @@ const whCacheOptions = {
};
const xyCacheOptions = {
_equal: equalXY,
_initialValue: { x: false, y: false },
_initialValue: { x: strHidden, y: strHidden } as XY<OverflowStyle>,
};
const getOverflowAmount = (
@@ -106,7 +108,7 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = (
const [updateOverflowAmountCache, getCurrentOverflowAmountCache] =
createCache<WH<number>>(whCacheOptions);
const [updateOverflowScrollCache] = createCache<XY<boolean>>(xyCacheOptions);
const [updateOverflowStyleCache] = createCache<XY<OverflowStyle>>(xyCacheOptions);
/**
* Applies a fixed height to the viewport so it can't overflow or underflow the host element.
@@ -152,33 +154,54 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = (
showNativeOverlaidScrollbars: boolean,
viewportStyleObj?: StyleObject
): ViewportOverflowState => {
const { x: overlaidX, y: overlaidY } = _nativeScrollbarIsOverlaid;
const determineOverflow = !viewportStyleObj;
const arrangeHideOffset =
!_nativeScrollbarStyling && !showNativeOverlaidScrollbars ? overlaidScrollbarsHideOffset : 0;
const styleObj = determineOverflow
? style(_viewport, ['overflowX', 'overflowY'])
: viewportStyleObj;
const scroll = {
x: styleObj.overflowX === 'scroll',
y: styleObj.overflowY === 'scroll',
};
const nonScrollbarStylingHideOffset = {
x: overlaidX ? arrangeHideOffset : _nativeScrollbarSize.x,
y: overlaidY ? arrangeHideOffset : _nativeScrollbarSize.y,
};
const scrollbarsHideOffset = {
x: scroll.x && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset.x : 0,
y: scroll.y && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset.y : 0,
const getStatePerAxis = (
styleKey: string,
isOverlaid: boolean,
nativeScrollbarSize: number
) => {
const overflowStyle = style(_viewport, styleKey);
// can't do something like "viewportStyleObj && viewportStyleObj[styleKey] || overflowStyle" here!
const objectPrefferedOverflowStyle = viewportStyleObj
? viewportStyleObj[styleKey]
: overflowStyle;
const overflowScroll = objectPrefferedOverflowStyle === 'scroll';
const nonScrollbarStylingHideOffset = isOverlaid ? arrangeHideOffset : nativeScrollbarSize;
const scrollbarsHideOffset =
overflowScroll && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset : 0;
const scrollbarsHideOffsetArrange = isOverlaid && !!arrangeHideOffset;
return [overflowStyle, overflowScroll, scrollbarsHideOffset, scrollbarsHideOffsetArrange] as [
overflowStyle: OverflowStyle,
overflowScroll: boolean,
scrollbarsHideOffset: number,
scrollbarsHideOffsetArrange: boolean
];
};
const [xOverflowStyle, xOverflowScroll, xScrollbarsHideOffset, xScrollbarsHideOffsetArrange] =
getStatePerAxis('overflowX', _nativeScrollbarIsOverlaid.x, _nativeScrollbarSize.x);
const [yOverflowStyle, yOverflowScroll, yScrollbarsHideOffset, yScrollbarsHideOffsetArrange] =
getStatePerAxis('overflowY', _nativeScrollbarIsOverlaid.y, _nativeScrollbarSize.y);
return {
_overflowScroll: scroll,
_scrollbarsHideOffsetArrange: {
x: overlaidX && !!arrangeHideOffset,
y: overlaidY && !!arrangeHideOffset,
_overflowStyle: {
x: xOverflowStyle,
y: yOverflowStyle,
},
_overflowScroll: {
x: xOverflowScroll,
y: yOverflowScroll,
},
_scrollbarsHideOffset: {
x: xScrollbarsHideOffset,
y: yScrollbarsHideOffset,
},
_scrollbarsHideOffsetArrange: {
x: xScrollbarsHideOffsetArrange,
y: yScrollbarsHideOffsetArrange,
},
_scrollbarsHideOffset: scrollbarsHideOffset,
};
};
@@ -416,7 +439,7 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = (
let sizeFractionCache = getCurrentSizeFraction(force);
let viewportScrollSizeCache = getCurrentViewportScrollSizeCache(force);
let overflowAmuntCache = getCurrentOverflowAmountCache(force);
let updateHintsReturn;
let preMeasureViewportOverflowState: ViewportOverflowState | undefined;
if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarStyling) {
@@ -527,9 +550,6 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = (
sizeFraction,
_directionIsRTL
);
const [overflowScroll, overflowScrollChanged] = updateOverflowScrollCache(
viewportOverflowState._overflowScroll
);
hideNativeScrollbars(viewportOverflowState, _directionIsRTL, viewportArranged, viewportStyle);
if (adjustFlexboxGlue) {
@@ -537,23 +557,28 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = (
}
style(_viewport, viewportStyle);
setState({
_overflowScroll: overflowScroll,
_overflowAmount: overflowAmount,
_hasOverflow: hasOverflow,
});
updateHintsReturn = {
_overflowAmountChanged: overflowAmountChanged,
_overflowScrollChanged: overflowScrollChanged,
};
}
attr(_host, dataAttributeHost, removeClipping ? 'overflowVisible' : '');
conditionalClass(_padding, classNameOverflowVisible, removeClipping);
conditionalClass(_viewport, classNameOverflowVisible, overflowVisible);
return updateHintsReturn;
const [overflowStyle, overflowStyleChanged] = updateOverflowStyleCache(
getViewportOverflowState(showNativeOverlaidScrollbars)._overflowStyle
);
setState({
_overflowStyle: overflowStyle,
_overflowAmount: {
x: overflowAmount.w,
y: overflowAmount.h,
},
_hasOverflow: hasOverflow,
});
return {
_overflowStyleChanged: overflowStyleChanged,
_overflowAmountChanged: overflowAmountChanged,
};
};
};
@@ -1,4 +1,5 @@
import { style } from 'support';
import { getEnvironment } from 'environment';
import type { CreateStructureUpdateSegment } from 'setups/structureSetup/structureSetup.update';
/**
@@ -14,10 +15,12 @@ export const createTrinsicUpdate: CreateStructureUpdateSegment = (
const [getState] = state;
return (updateHints) => {
const { _flexboxGlue } = getEnvironment();
const { _heightIntrinsic } = getState();
const { _heightIntrinsicChanged } = updateHints;
const heightIntrinsicChanged = (_content || !_flexboxGlue) && _heightIntrinsicChanged;
if (_heightIntrinsicChanged) {
if (heightIntrinsicChanged) {
style(_content, {
height: _heightIntrinsic ? '' : '100%',
display: _heightIntrinsic ? '' : 'inline',
@@ -25,8 +28,8 @@ export const createTrinsicUpdate: CreateStructureUpdateSegment = (
}
return {
_sizeChanged: _heightIntrinsicChanged,
_contentMutation: _heightIntrinsicChanged,
_sizeChanged: heightIntrinsicChanged,
_contentMutation: heightIntrinsicChanged,
};
};
};
@@ -67,6 +67,8 @@ export interface OSInitializationObject extends StructureInitialization, Scrollb
export type OSTarget = OSTargetElement | OSInitializationObject;
export type OverflowStyle = 'scroll' | 'hidden' | 'visible';
/*
export namespace OverlayScrollbars {
export type ResizeBehavior = 'none' | 'both' | 'horizontal' | 'vertical';
@@ -0,0 +1,102 @@
import 'styles/overlayscrollbars.scss';
import './index.scss';
import should from 'should';
import { OverlayScrollbars } from 'overlayscrollbars';
import { resize } from '@/testing-browser/Resize';
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
import { addClass } from 'support';
OverlayScrollbars.env().setDefaultOptions({
nativeScrollbarsOverlaid: { initialize: true },
});
const startBtn: HTMLButtonElement | null = document.querySelector('#start');
const targetRoot: HTMLElement | null = document.querySelector('#targetRoot');
const targetA: HTMLElement | null = document.querySelector('#targetA');
const targetB: HTMLElement | null = document.querySelector('#targetB');
const updatesRootSlot: HTMLElement | null = document.querySelector('#updatesRoot');
const updatesASlot: HTMLElement | null = document.querySelector('#updatesA');
const updatesBSlot: HTMLElement | null = document.querySelector('#updatesB');
const resizeRoot: HTMLElement | null = document.querySelector('#resizeRoot');
const resizeA: HTMLElement | null = document.querySelector('#resizeA');
const resizeB: HTMLElement | null = document.querySelector('#resizeB');
const resizeBetweenRoot: HTMLElement | null = document.createElement('div');
const resizeBetweenA: HTMLElement | null = document.createElement('div');
const resizeBetweenB: HTMLElement | null = document.createElement('div');
let rootUpdateCount = 0;
let aUpdateCount = 0;
let bUpdateCount = 0;
const osInstanceRoot = OverlayScrollbars(
targetRoot!,
{},
{
initialized() {
addClass(resizeBetweenRoot, 'resize resizeBetween');
targetRoot!.append(resizeBetweenRoot);
},
updated() {
rootUpdateCount++;
requestAnimationFrame(() => {
if (updatesRootSlot) {
updatesRootSlot.textContent = `${rootUpdateCount}`;
}
});
},
}
);
const osInstanceA = OverlayScrollbars(
targetA!,
{},
{
initialized() {
addClass(resizeBetweenA, 'resize resizeBetween');
targetA!.append(resizeBetweenA);
},
updated(args) {
console.log(args);
aUpdateCount++;
requestAnimationFrame(() => {
if (updatesASlot) {
updatesASlot.textContent = `${aUpdateCount}`;
}
});
},
}
);
const osInstanceB = OverlayScrollbars(
targetB!,
{},
{
initialized() {
addClass(resizeBetweenB, 'resize resizeBetween');
targetB!.append(resizeBetweenB);
},
updated() {
bUpdateCount++;
requestAnimationFrame(() => {
if (updatesBSlot) {
updatesBSlot.textContent = `${bUpdateCount}`;
}
});
},
}
);
resize(resizeRoot!);
resize(resizeA!);
resize(resizeB!);
resize(resizeBetweenRoot!);
resize(resizeBetweenA!);
resize(resizeBetweenB!);
const start = async () => {
setTestResult(null);
// target?.removeAttribute('style');
setTestResult(true);
};
startBtn?.addEventListener('click', start);
@@ -0,0 +1,19 @@
<div id="controls">
<button id="start">start</button>
<div>UpdatesRoot: <span id="updatesRoot"></span></div>
<div>UpdatesA: <span id="updatesA"></span></div>
<div>UpdatesB:</div>
</div>
<div id="stage">
<div>
<div id="targetRoot" class="container">
<div id="targetA" class="container">
<div id="targetB" class="container">
<div id="resizeB" class="resize">B <span id="updatesB"></span></div>
</div>
<div id="resizeA" class="resize">A</div>
</div>
<div id="resizeRoot" class="resize">Root</div>
</div>
</div>
</div>
@@ -0,0 +1,60 @@
body {
display: flex;
flex-direction: column;
overflow: scroll;
}
#controls {
flex: none;
}
#stage {
flex: auto;
position: relative;
& > div {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: lightgoldenrodyellow;
}
}
.container {
border: 1px solid red;
width: 60%;
height: 60%;
padding: 10px;
}
.resize {
overflow: hidden;
background: lime;
border: 1px solid green;
padding: 10px;
}
.resizer {
position: relative;
}
.resizeBetween {
background: tomato;
position: absolute;
bottom: 0;
right: 0;
}
.resizeBtn {
position: absolute;
bottom: 0;
right: 0;
height: 20px;
width: 20px;
background: blue;
opacity: 0.3;
}
#targetRoot {
height: 80%;
}
@@ -4,7 +4,7 @@ import { test, Page } from '@playwright/test';
playwrightRollup();
test.describe('StructureLifecycle', () => {
test.describe('StructureSetup.update', () => {
[false, true].forEach(async (nativeScrollbarStyling) => {
const withText = nativeScrollbarStyling ? 'with' : 'without';
const nss = async (page: Page) => {
@@ -155,14 +155,14 @@ const getMetrics = (elm: HTMLElement): Metrics => {
},
scroll: elmIsTarget
? {
width: Math.round(osInstance.state()._overflowAmount.w),
height: Math.round(osInstance.state()._overflowAmount.h),
width: Math.round(osInstance.state().overflowAmount.x),
height: Math.round(osInstance.state().overflowAmount.y),
}
: scrollMeasure(),
hasOverflow: elmIsTarget
? {
x: osInstance.state()._overflowAmount.w > 0,
y: osInstance.state()._overflowAmount.h > 0,
x: osInstance.state().hasOverflow.x,
y: osInstance.state().hasOverflow.y,
}
: {
x: scrollMeasure().width > 0,
@@ -274,12 +274,12 @@ const checkMetrics = async (checkComparison: CheckComparisonObj) => {
should.equal(
targetMetrics.scroll.width,
comparisonMetrics.scroll.width,
`Scroll width equality. (${osInstance.state()._overflowAmount.w})`
`Scroll width equality. (${osInstance.state().overflowAmount.x})`
);
should.equal(
targetMetrics.scroll.height,
comparisonMetrics.scroll.height,
`Scroll height equality. (${osInstance.state()._overflowAmount.h})`
`Scroll height equality. (${osInstance.state().overflowAmount.y})`
);
should.equal(
@@ -295,24 +295,24 @@ const checkMetrics = async (checkComparison: CheckComparisonObj) => {
if (targetMetrics.hasOverflow.x) {
should.ok(
osInstance.state()._overflowAmount.w > 0,
osInstance.state().overflowAmount.x > 0,
'Overflow amount width should be > 0 with overflow.'
);
} else {
should.equal(
osInstance.state()._overflowAmount.w,
osInstance.state().overflowAmount.x,
0,
'Overflow amount width should be 0 without overflow.'
);
}
if (targetMetrics.hasOverflow.y) {
should.ok(
osInstance.state()._overflowAmount.h > 0,
osInstance.state().overflowAmount.y > 0,
'Overflow amount height should be > 0 with overflow.'
);
} else {
should.equal(
osInstance.state()._overflowAmount.h,
osInstance.state().overflowAmount.y,
0,
'Overflow amount height should be 0 without overflow.'
);
@@ -414,6 +414,17 @@ const checkMetrics = async (checkComparison: CheckComparisonObj) => {
should.notEqual(viewportOverflowYStyle, 'scroll', 'No Overflow-Y shouldnt result in scroll.');
}
should.equal(
osInstance.state().overflowStyle.x,
viewportOverflowXStyle,
'Overflow-X Style: State and style should match.'
);
should.equal(
osInstance.state().overflowStyle.y,
viewportOverflowYStyle,
'Overflow-Y Style: State and style should match.'
);
// ==== check host & padding overflow style:
if (!targetMetrics.hasOverflow.x && !targetMetrics.hasOverflow.y) {
@@ -712,18 +723,23 @@ const start = async () => {
setTestResult(null);
target?.removeAttribute('style');
await overflowTest();
await overflowTest({ overflow: { x: 'hidden', y: 'scroll' } });
await overflowTest({ overflow: { x: 'scroll', y: 'hidden' } });
await overflowTest({ overflow: { x: 'visible', y: 'scroll' } });
await overflowTest({ overflow: { x: 'scroll', y: 'visible' } });
const start = performance.now();
console.log(start);
await overflowTest();
await overflowTest({ overflow: { x: 'visible', y: 'visible' } });
await overflowTest({ overflow: { x: 'visible-scroll', y: 'visible-hidden' } });
await overflowTest({ overflow: { x: 'visible-hidden', y: 'hidden' } });
await overflowTest({ overflow: { x: 'visible', y: 'visible-scroll' } });
await overflowTest({ overflow: { x: 'scroll', y: 'visible-scroll' } });
await overflowTest({ overflow: { x: 'hidden', y: 'scroll' } });
await overflowTest({ overflow: { x: 'scroll', y: 'hidden' } });
await overflowTest({ overflow: { x: 'visible', y: 'scroll' } });
await overflowTest({ overflow: { x: 'scroll', y: 'visible' } });
await overflowTest({ overflow: { x: 'visible', y: 'hidden' } });
await overflowTest({ overflow: { x: 'hidden', y: 'visible' } });
const end = performance.now();
console.log(start - end);
setTestResult(true);
};
@@ -0,0 +1,58 @@
// @ts-ignore
import { playwrightRollup, expectSuccess } from '@/playwright/rollup';
import { test, Page } from '@playwright/test';
playwrightRollup();
test.describe('StructureSetup.update', () => {
[false, true].forEach(async (nativeScrollbarStyling) => {
const withText = nativeScrollbarStyling ? 'with' : 'without';
const nss = async (page: Page) => {
if (!nativeScrollbarStyling) {
await page.click('#nss');
await page.waitForTimeout(500);
}
};
test.describe(`${withText} native scrollbar styling`, () => {
test.describe.configure({ mode: 'parallel' });
test('default', async ({ page }) => {
await nss(page);
await page.click('#start');
await expectSuccess(page);
});
test('without flexbox glue & css custom props', async ({ page }) => {
await nss(page);
await page.click('#fbg');
await page.waitForTimeout(500);
await page.click('#ccp');
await page.waitForTimeout(500);
await page.click('#start');
await expectSuccess(page);
});
test('with partially overlaid scrollbars', async ({ page, browserName }) => {
test.skip(
browserName === 'firefox' || browserName === 'webkit',
"firefox can't simulate partially overlaid scrollbars, boost speed by omitting webkit"
);
await nss(page);
await page.click('#po');
await page.waitForTimeout(500);
await page.click('#start');
await expectSuccess(page);
});
test('with fully overlaid scrollbars', async ({ page }) => {
await nss(page);
await page.click('#fo');
await page.waitForTimeout(500);
await page.click('#start');
await expectSuccess(page);
});
});
});
});
+85 -28
View File
@@ -41,6 +41,27 @@ interface ScrollbarsInitialization {
interface OSInitializationObject extends StructureInitialization, ScrollbarsInitialization {
}
type OSTarget = OSTargetElement | OSInitializationObject;
type OverflowStyle = "scroll" | "hidden" | "visible";
interface TRBL {
t: number;
r: number;
b: number;
l: number;
}
interface XY<T> {
x: T;
y: T;
}
type EventListener<NameArgsMap extends Record<string, any>, Name extends Extract<keyof NameArgsMap, string>> = (...args: NameArgsMap[Name] extends undefined ? [
] : [
args: NameArgsMap[Name]
]) => void;
type EventListenerGroup<NameArgsMap extends Record<string, any>, Name extends Extract<keyof NameArgsMap, string>> = EventListener<NameArgsMap, Name> | EventListener<NameArgsMap, Name>[];
type AddEventListener<NameArgsMap extends Record<string, any>> = <Name extends Extract<keyof NameArgsMap, string>>(name: Name, listener: EventListenerGroup<NameArgsMap, Name>) => () => void;
type RemoveEventListener<NameArgsMap extends Record<string, any>> = <Name extends Extract<keyof NameArgsMap, string>>(name?: Name, listener?: EventListenerGroup<NameArgsMap, Name>) => void;
type InitialEventListeners<NameArgsMap extends Record<string, any>> = {
[K in Extract<keyof NameArgsMap, string>]?: EventListenerGroup<NameArgsMap, K>;
};
type ResizeBehavior = "none" | "both" | "horizontal" | "vertical";
type OverflowBehavior = "hidden" | "scroll" | "visible" | "visible-hidden" | "visible-scroll";
type VisibilityBehavior = "visible" | "hidden" | "auto";
@@ -84,25 +105,43 @@ interface OSOptions {
onUpdated: (() => any) | null;
};
}
type StructureInitializationStrategyElementFn<T> = ((target: OSTargetElement) => HTMLElement | T) | T;
type ScrollbarsInitializationStrategyElementFn<T> = ((target: OSTargetElement, host: HTMLElement, viewport: HTMLElement) => HTMLElement | T) | T;
/**
* A Static element is an element which MUST be generated.
* If null or undefined (or the returned result is null or undefined), the initialization function is generatig the element, otherwise
* the element returned by the function acts as the generated element.
*/
type StructureInitializationStrategyStaticElement = StructureInitializationStrategyElementFn<null | undefined>;
/**
* A Dynamic element is an element which CAN be generated.
* If boolean (or the returned result is boolean), the generation of the element is forced (or not).
* If the function returns and element, the element returned by the function acts as the generated element.
*/
type StructureInitializationStrategyDynamicElement = StructureInitializationStrategyElementFn<boolean>;
interface StructureInitializationStrategy {
_host: StructureInitializationStrategyStaticElement;
_viewport: StructureInitializationStrategyStaticElement;
_padding: StructureInitializationStrategyDynamicElement;
_content: StructureInitializationStrategyDynamicElement;
}
interface ScrollbarsInitializationStrategy {
/**
* The scrollbars slot. If null or undefined (or the returned result is null or undefined), the initialization function is deciding the element, otherwise
* the element returned by the function acts as the scrollbars slot.
*/
_scrollbarsSlot: ScrollbarsInitializationStrategyElementFn<null | undefined>;
}
interface InitializationStrategy extends StructureInitializationStrategy, ScrollbarsInitializationStrategy {
}
type DefaultInitializationStrategy = {
[K in keyof InitializationStrategy]: Extract<InitializationStrategy[K], boolean | null | undefined>;
};
type OSPluginInstance = Record<string, unknown> | ((staticObj: OverlayScrollbarsStatic, instanceObj: OverlayScrollbars) => void);
type OSPlugin<T extends OSPluginInstance> = [
string,
T
];
interface XY<T> {
x: T;
y: T;
}
type EventListener<NameArgsMap extends Record<string, any>, Name extends Extract<keyof NameArgsMap, string>> = (...args: NameArgsMap[Name] extends undefined ? [
] : [
args: NameArgsMap[Name]
]) => void;
type EventListenerGroup<NameArgsMap extends Record<string, any>, Name extends Extract<keyof NameArgsMap, string>> = EventListener<NameArgsMap, Name> | EventListener<NameArgsMap, Name>[];
type AddEventListener<NameArgsMap extends Record<string, any>> = <Name extends Extract<keyof NameArgsMap, string>>(name: Name, listener: EventListenerGroup<NameArgsMap, Name>) => () => void;
type RemoveEventListener<NameArgsMap extends Record<string, any>> = <Name extends Extract<keyof NameArgsMap, string>>(name?: Name, listener?: EventListenerGroup<NameArgsMap, Name>) => void;
type InitialEventListeners<NameArgsMap extends Record<string, any>> = {
[K in Extract<keyof NameArgsMap, string>]?: EventListenerGroup<NameArgsMap, K>;
};
/*
onScrollStart : null,
onScroll : null,
@@ -119,27 +158,16 @@ interface OnUpdatedEventListenerArgs {
directionChanged: boolean;
heightIntrinsicChanged: boolean;
overflowAmountChanged: boolean;
overflowScrollChanged: boolean;
overflowStyleChanged: boolean;
hostMutation: boolean;
contentMutation: boolean;
};
changedOptions: PartialOptions<OSOptions>;
force: boolean;
}
interface OnOverflowChangedEventListenerArgs {
overflow: XY<boolean>; // whether there is an overflow
scrollableOverflow: XY<boolean>; // whether there is an scrollable overflow
amount: XY<number>; // the overflow amount in pixel
previous: {
overflow: XY<boolean>;
scrollableOverflow: XY<boolean>;
amount: XY<number>;
};
}
interface OSEventListenersNameArgsMap {
initialized: undefined;
initializationWithdrawn: undefined;
overflowChanged: OnOverflowChangedEventListenerArgs;
updated: OnUpdatedEventListenerArgs;
destroyed: undefined;
}
@@ -148,16 +176,45 @@ type RemoveOSEventListener = RemoveEventListener<OSEventListenersNameArgsMap>;
type InitialOSEventListeners = InitialEventListeners<OSEventListenersNameArgsMap>;
interface OverlayScrollbarsStatic {
(target: OSTarget | OSInitializationObject, options?: PartialOptions<OSOptions>, eventListeners?: InitialOSEventListeners): OverlayScrollbars;
extend(osPlugin: OSPlugin | OSPlugin[]): void;
plugin(osPlugin: OSPlugin | OSPlugin[]): void;
env(): OverlayScrollbarsEnv;
}
interface OverlayScrollbarsEnv {
scrollbarSize: XY<number>;
scrollbarIsOverlaid: XY<boolean>;
scrollbarStyling: boolean;
rtlScrollBehavior: {
n: boolean;
i: boolean;
};
flexboxGlue: boolean;
cssCustomProperties: boolean;
defaultInitializationStrategy: DefaultInitializationStrategy;
defaultDefaultOptions: OSOptions;
getInitializationStrategy(): InitializationStrategy;
setInitializationStrategy(newInitializationStrategy: Partial<InitializationStrategy>): void;
getDefaultOptions(): OSOptions;
setDefaultOptions(newDefaultOptions: PartialOptions<OSOptions>): void;
}
interface OverlayScrollbarsState {
padding: TRBL;
paddingAbsolute: boolean;
overflowAmount: XY<number>;
overflowStyle: XY<OverflowStyle>;
hasOverflow: XY<boolean>;
}
interface OverlayScrollbars {
options(): OSOptions;
options(newOptions?: PartialOptions<OSOptions>): OSOptions;
update(force?: boolean): void;
destroy(): void;
state(): any;
state(): OverlayScrollbarsState;
on: AddOSEventListener;
off: RemoveOSEventListener;
}
/**
* Notes:
* Height intrinsic detection use "content: true" init strategy - or open ticket for custom height intrinsic observer
*/
declare const OverlayScrollbars: OverlayScrollbarsStatic;
export { OverlayScrollbars as default };
+1 -1
View File
@@ -2,7 +2,7 @@ const { devices } = require('@playwright/test');
module.exports = {
testMatch: /.*\/tests\/playwright\/.*\.test\.[jt]sx?/,
timeout: 5 * 60 * 1000,
timeout: 8 * 60 * 1000,
actionTimeout: 300,
navigationTimeout: 1000,
retries: 0,