improve code, add env, test nesting

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