mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-05-29 18:44:08 +03:00
improve code, add env, test nesting
This commit is contained in:
+146
-117
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
+65
-40
@@ -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,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
+6
-3
@@ -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';
|
||||
|
||||
+102
@@ -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%;
|
||||
}
|
||||
+1
-1
@@ -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) => {
|
||||
+32
-16
@@ -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);
|
||||
};
|
||||
|
||||
+58
@@ -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
@@ -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 };
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user