add viewport is target mode

This commit is contained in:
Rene
2022-07-07 13:27:31 +02:00
parent 66c55f98e9
commit b581c91738
17 changed files with 643 additions and 336 deletions
+189 -111
View File
@@ -195,6 +195,17 @@ function attr(elm, attrName, value) {
elm && elm.setAttribute(attrName, value); elm && elm.setAttribute(attrName, value);
} }
const attrClass = (elm, attrName, value, add) => {
const currValues = attr(elm, attrName) || '';
const currValuesSet = new Set(currValues.split(' '));
currValuesSet[add ? 'add' : 'delete'](value);
attr(elm, attrName, from(currValuesSet).join(' ').trim());
};
const hasAttrClass = (elm, attrName, value) => {
const currValues = attr(elm, attrName) || '';
const currValuesSet = new Set(currValues.split(' '));
return currValuesSet.has(value);
};
const removeAttr = (elm, attrName) => { const removeAttr = (elm, attrName) => {
elm && elm.removeAttribute(attrName); elm && elm.removeAttribute(attrName);
}; };
@@ -213,6 +224,11 @@ const find = (selector, elm) => {
return rootElm ? push(arr, rootElm.querySelectorAll(selector)) : arr; return rootElm ? push(arr, rootElm.querySelectorAll(selector)) : arr;
}; };
const findFirst = (selector, elm) => {
const rootElm = elm ? isElement(elm) ? elm : null : document;
return rootElm ? rootElm.querySelector(selector) : null;
};
const is = (elm, selector) => { const is = (elm, selector) => {
if (isElement(elm)) { if (isElement(elm)) {
const fn = elmPrototype.matches || elmPrototype.msMatchesSelector; const fn = elmPrototype.matches || elmPrototype.msMatchesSelector;
@@ -226,6 +242,32 @@ const contents = elm => elm ? from(elm.childNodes) : [];
const parent = elm => elm ? elm.parentElement : null; const parent = elm => elm ? elm.parentElement : null;
const closest = (elm, selector) => {
if (isElement(elm)) {
const closestFn = elmPrototype.closest;
if (closestFn) {
return closestFn.call(elm, selector);
}
do {
if (is(elm, selector)) {
return elm;
}
elm = parent(elm);
} while (elm);
}
return null;
};
const liesBetween = (elm, highBoundarySelector, deepBoundarySelector) => {
const closestHighBoundaryElm = elm && closest(elm, highBoundarySelector);
const closestDeepBoundaryElm = elm && findFirst(deepBoundarySelector, closestHighBoundaryElm);
return closestHighBoundaryElm && closestDeepBoundaryElm ? closestHighBoundaryElm === elm || closestDeepBoundaryElm === elm || closest(closest(elm, deepBoundarySelector), highBoundarySelector) !== closestHighBoundaryElm : false;
};
const before = (parentElm, preferredAnchor, insertedElms) => { const before = (parentElm, preferredAnchor, insertedElms) => {
if (insertedElms) { if (insertedElms) {
let anchor = preferredAnchor; let anchor = preferredAnchor;
@@ -349,7 +391,7 @@ const classListAction = (elm, className, action) => {
let i = 0; let i = 0;
let result = false; let result = false;
if (elm && isString(className)) { if (elm && className && isString(className)) {
const classes = className.match(rnothtmlwhite) || []; const classes = className.match(rnothtmlwhite) || [];
result = classes.length > 0; result = classes.length > 0;
@@ -360,6 +402,8 @@ const classListAction = (elm, className, action) => {
return result; return result;
}; };
const hasClass = (elm, className) => classListAction(elm, className, (classList, clazz) => classList.contains(clazz));
const removeClass = (elm, className) => { const removeClass = (elm, className) => {
classListAction(elm, className, (classList, clazz) => classList.remove(clazz)); classListAction(elm, className, (classList, clazz) => classList.remove(clazz));
}; };
@@ -367,22 +411,6 @@ const addClass = (elm, className) => {
classListAction(elm, className, (classList, clazz) => classList.add(clazz)); classListAction(elm, className, (classList, clazz) => classList.add(clazz));
return removeClass.bind(0, elm, className); return removeClass.bind(0, elm, className);
}; };
const diffClass = (classNameA, classNameB) => {
const classNameASplit = classNameA && classNameA.split(' ');
const classNameBSplit = classNameB && classNameB.split(' ');
const tempObj = {};
each(classNameASplit, className => {
tempObj[className] = 1;
});
each(classNameBSplit, className => {
if (tempObj[className]) {
delete tempObj[className];
} else {
tempObj[className] = 1;
}
});
return keys(tempObj);
};
const equal = (a, b, props, propMutation) => { const equal = (a, b, props, propMutation) => {
if (a && b) { if (a && b) {
@@ -688,6 +716,10 @@ const classNameEnvironment = 'os-environment';
const classNameEnvironmentFlexboxGlue = `${classNameEnvironment}-flexbox-glue`; const classNameEnvironmentFlexboxGlue = `${classNameEnvironment}-flexbox-glue`;
const classNameEnvironmentFlexboxGlueMax = `${classNameEnvironmentFlexboxGlue}-max`; const classNameEnvironmentFlexboxGlueMax = `${classNameEnvironmentFlexboxGlue}-max`;
const dataAttributeHost = 'data-overlayscrollbars'; const dataAttributeHost = 'data-overlayscrollbars';
const dataAttributeHostOverflowX = `${dataAttributeHost}-overflow-x`;
const dataAttributeHostOverflowY = `${dataAttributeHost}-overflow-y`;
const dataValueHostOverflowVisible = 'overflowVisible';
const dataValueHostViewportScrollbarStyling = 'viewportStyled';
const classNamePadding = 'os-padding'; const classNamePadding = 'os-padding';
const classNameViewport = 'os-viewport'; const classNameViewport = 'os-viewport';
const classNameViewportArrange = `${classNameViewport}-arrange`; const classNameViewportArrange = `${classNameViewport}-arrange`;
@@ -716,17 +748,21 @@ const stringify = value => JSON.stringify(value, (_, val) => {
}); });
const defaultOptions = { const defaultOptions = {
resize: 'none',
paddingAbsolute: false, paddingAbsolute: false,
updating: { updating: {
elementEvents: [['img', 'load']], elementEvents: [['img', 'load']],
debounce: [0, 33],
attributes: null, attributes: null,
debounce: [0, 33] ignoreMutation: null
}, },
overflow: { overflow: {
x: 'scroll', x: 'scroll',
y: 'scroll' y: 'scroll'
}, },
nativeScrollbarsOverlaid: {
show: false,
initialize: false
},
scrollbars: { scrollbars: {
visibility: 'auto', visibility: 'auto',
autoHide: 'never', autoHide: 'never',
@@ -734,18 +770,6 @@ const defaultOptions = {
dragScroll: true, dragScroll: true,
clickScroll: false, clickScroll: false,
touch: true touch: true
},
textarea: {
dynWidth: false,
dynHeight: false,
inheritedAttrs: ['style', 'class']
},
nativeScrollbarsOverlaid: {
show: false,
initialize: false
},
callbacks: {
onUpdated: null
} }
}; };
const getOptionsDiff = (currOptions, newOptions) => { const getOptionsDiff = (currOptions, newOptions) => {
@@ -988,8 +1012,8 @@ const dynamicCreationFromStrategy = (target, initializationValue, strategy) => {
return result === true ? createDiv() : result; return result === true ? createDiv() : result;
}; };
const addDataAttrHost = elm => { const addDataAttrHost = (elm, value) => {
attr(elm, dataAttributeHost, ''); attr(elm, dataAttributeHost, value || '');
return removeAttr.bind(0, elm, dataAttributeHost); return removeAttr.bind(0, elm, dataAttributeHost);
}; };
@@ -1014,20 +1038,28 @@ const createStructureSetupElements = target => {
const ownerDocument = targetElement.ownerDocument; const ownerDocument = targetElement.ownerDocument;
const bodyElm = ownerDocument.body; const bodyElm = ownerDocument.body;
const wnd = ownerDocument.defaultView; const wnd = ownerDocument.defaultView;
const singleElmSupport = !!ResizeObserverConstructor && _nativeScrollbarStyling;
const potentialViewportElement = staticCreationFromStrategy(targetElement, targetStructureInitialization.viewport, viewportInitializationStrategy);
const potentiallySingleElm = potentialViewportElement === targetElement;
const viewportIsTarget = singleElmSupport && potentiallySingleElm;
const viewportElement = potentiallySingleElm && !viewportIsTarget ? staticCreationFromStrategy(targetElement) : potentialViewportElement;
const evaluatedTargetObj = { const evaluatedTargetObj = {
_target: targetElement, _target: targetElement,
_host: isTextarea ? staticCreationFromStrategy(targetElement, targetStructureInitialization.host, hostInitializationStrategy) : targetElement, _host: isTextarea ? staticCreationFromStrategy(targetElement, targetStructureInitialization.host, hostInitializationStrategy) : targetElement,
_viewport: staticCreationFromStrategy(targetElement, targetStructureInitialization.viewport, viewportInitializationStrategy), _viewport: viewportElement,
_padding: dynamicCreationFromStrategy(targetElement, targetStructureInitialization.padding, paddingInitializationStrategy), _padding: dynamicCreationFromStrategy(targetElement, targetStructureInitialization.padding, paddingInitializationStrategy),
_content: dynamicCreationFromStrategy(targetElement, targetStructureInitialization.content, contentInitializationStrategy), _content: dynamicCreationFromStrategy(targetElement, targetStructureInitialization.content, contentInitializationStrategy),
_viewportArrange: createUniqueViewportArrangeElement(), _viewportArrange: !viewportIsTarget && createUniqueViewportArrangeElement(),
_windowElm: wnd, _windowElm: wnd,
_documentElm: ownerDocument, _documentElm: ownerDocument,
_htmlElm: parent(bodyElm), _htmlElm: parent(bodyElm),
_bodyElm: bodyElm, _bodyElm: bodyElm,
_isTextarea: isTextarea, _isTextarea: isTextarea,
_isBody: isBody, _isBody: isBody,
_targetIsElm: targetIsElm _targetIsElm: targetIsElm,
_viewportIsTarget: viewportIsTarget,
_viewportHasClass: (className, attributeClassName) => viewportIsTarget ? hasAttrClass(viewportElement, dataAttributeHost, attributeClassName) : hasClass(viewportElement, className),
_viewportAddRemoveClass: (className, attributeClassName, add) => viewportIsTarget ? attrClass(viewportElement, dataAttributeHost, attributeClassName, add) : (add ? addClass : removeClass)(viewportElement, className)
}; };
const generatedElements = keys(evaluatedTargetObj).reduce((arr, key) => { const generatedElements = keys(evaluatedTargetObj).reduce((arr, key) => {
const value = evaluatedTargetObj[key]; const value = evaluatedTargetObj[key];
@@ -1048,9 +1080,9 @@ const createStructureSetupElements = target => {
const isTextareaHostGenerated = isTextarea && elementIsGenerated(_host); const isTextareaHostGenerated = isTextarea && elementIsGenerated(_host);
const targetContents = isTextarea ? _target : contents([_content, _viewport, _padding, _host, _target].find(elm => elementIsGenerated(elm) === false)); const targetContents = isTextarea ? _target : contents([_content, _viewport, _padding, _host, _target].find(elm => elementIsGenerated(elm) === false));
const contentSlot = _content || _viewport; const contentSlot = _content || _viewport;
const removeHostDataAttr = addDataAttrHost(_host); const removeHostDataAttr = addDataAttrHost(_host, viewportIsTarget ? 'viewport' : 'host');
const removePaddingClass = addClass(_padding, classNamePadding); const removePaddingClass = addClass(_padding, classNamePadding);
const removeViewportClass = addClass(_viewport, classNameViewport); const removeViewportClass = addClass(_viewport, !viewportIsTarget && classNameViewport);
const removeContentClass = addClass(_content, classNameContent); const removeContentClass = addClass(_content, classNameContent);
if (isTextareaHostGenerated) { if (isTextareaHostGenerated) {
@@ -1063,34 +1095,31 @@ const createStructureSetupElements = target => {
appendChildren(contentSlot, targetContents); appendChildren(contentSlot, targetContents);
appendChildren(_host, _padding); appendChildren(_host, _padding);
appendChildren(_padding || _host, _viewport); appendChildren(_padding || _host, !viewportIsTarget && _viewport);
appendChildren(_viewport, _content); appendChildren(_viewport, _content);
push(destroyFns, () => { push(destroyFns, () => {
if (targetIsElm) { removeHostDataAttr();
appendChildren(_host, contents(contentSlot)); removeAttr(_viewport, dataAttributeHostOverflowX);
removeElements(_padding || _viewport); removeAttr(_viewport, dataAttributeHostOverflowY);
removeHostDataAttr();
} else {
if (elementIsGenerated(_content)) {
unwrap(_content);
}
if (elementIsGenerated(_viewport)) { if (elementIsGenerated(_content)) {
unwrap(_viewport); unwrap(_content);
}
if (elementIsGenerated(_padding)) {
unwrap(_padding);
}
removeHostDataAttr();
removePaddingClass();
removeViewportClass();
removeContentClass();
} }
if (elementIsGenerated(_viewport)) {
unwrap(_viewport);
}
if (elementIsGenerated(_padding)) {
unwrap(_padding);
}
removePaddingClass();
removeViewportClass();
removeContentClass();
}); });
if (_nativeScrollbarStyling) { if (_nativeScrollbarStyling && !viewportIsTarget) {
push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling)); push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling));
} }
@@ -1121,8 +1150,7 @@ const createTrinsicUpdate = (structureSetupElements, state) => {
if (heightIntrinsicChanged) { if (heightIntrinsicChanged) {
style(_content, { style(_content, {
height: _heightIntrinsic ? '' : '100%', height: _heightIntrinsic ? '' : '100%'
display: _heightIntrinsic ? '' : 'inline'
}); });
} }
@@ -1138,7 +1166,8 @@ const createPaddingUpdate = (structureSetupElements, state) => {
const { const {
_host, _host,
_padding, _padding,
_viewport _viewport,
_viewportIsTarget: _isSingleElm
} = structureSetupElements; } = structureSetupElements;
const [updatePaddingCache, currentPaddingCache] = createCache({ const [updatePaddingCache, currentPaddingCache] = createCache({
_equal: equalTRBL, _equal: equalTRBL,
@@ -1165,7 +1194,7 @@ const createPaddingUpdate = (structureSetupElements, state) => {
[padding, paddingChanged] = updatePaddingCache(force); [padding, paddingChanged] = updatePaddingCache(force);
} }
const paddingStyleChanged = paddingAbsoluteChanged || _directionChanged || paddingChanged; const paddingStyleChanged = !_isSingleElm && (paddingAbsoluteChanged || _directionChanged || paddingChanged);
if (paddingStyleChanged) { if (paddingStyleChanged) {
const paddingRelative = !paddingAbsolute || !_padding && !_nativeScrollbarStyling; const paddingRelative = !paddingAbsolute || !_padding && !_nativeScrollbarStyling;
@@ -1234,7 +1263,7 @@ const getOverflowAmount = (viewportScrollSize, viewportClientSize, sizeFraction)
}; };
}; };
const conditionalClass = (elm, classNames, condition) => condition ? addClass(elm, classNames) : removeClass(elm, classNames); const conditionalClass = (elm, classNames, add) => add ? addClass(elm, classNames) : removeClass(elm, classNames);
const overflowIsVisible = overflowBehavior => overflowBehavior.indexOf(strVisible) === 0; const overflowIsVisible = overflowBehavior => overflowBehavior.indexOf(strVisible) === 0;
@@ -1244,7 +1273,9 @@ const createOverflowUpdate = (structureSetupElements, state) => {
_host, _host,
_padding, _padding,
_viewport, _viewport,
_viewportArrange _viewportArrange,
_viewportIsTarget,
_viewportAddRemoveClass
} = structureSetupElements; } = structureSetupElements;
const { const {
_nativeScrollbarSize, _nativeScrollbarSize,
@@ -1252,7 +1283,7 @@ const createOverflowUpdate = (structureSetupElements, state) => {
_nativeScrollbarStyling, _nativeScrollbarStyling,
_nativeScrollbarIsOverlaid _nativeScrollbarIsOverlaid
} = getEnvironment(); } = getEnvironment();
const doViewportArrange = !_nativeScrollbarStyling && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y); const doViewportArrange = !_viewportIsTarget && !_nativeScrollbarStyling && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y);
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);
@@ -1483,7 +1514,7 @@ const createOverflowUpdate = (structureSetupElements, state) => {
const [showNativeOverlaidScrollbarsOption, showNativeOverlaidScrollbarsChanged] = checkOption('nativeScrollbarsOverlaid.show'); const [showNativeOverlaidScrollbarsOption, showNativeOverlaidScrollbarsChanged] = checkOption('nativeScrollbarsOverlaid.show');
const [overflow, overflowChanged] = checkOption('overflow'); const [overflow, overflowChanged] = checkOption('overflow');
const showNativeOverlaidScrollbars = showNativeOverlaidScrollbarsOption && _nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y; const showNativeOverlaidScrollbars = showNativeOverlaidScrollbarsOption && _nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y;
const adjustFlexboxGlue = !_flexboxGlue && (_sizeChanged || _contentMutation || _hostMutation || showNativeOverlaidScrollbarsChanged || _heightIntrinsicChanged); const adjustFlexboxGlue = !_viewportIsTarget && !_flexboxGlue && (_sizeChanged || _contentMutation || _hostMutation || showNativeOverlaidScrollbarsChanged || _heightIntrinsicChanged);
const overflowXVisible = overflowIsVisible(overflow.x); const overflowXVisible = overflowIsVisible(overflow.x);
const overflowYVisible = overflowIsVisible(overflow.y); const overflowYVisible = overflowIsVisible(overflow.y);
const overflowVisible = overflowXVisible || overflowYVisible; const overflowVisible = overflowXVisible || overflowYVisible;
@@ -1493,7 +1524,7 @@ const createOverflowUpdate = (structureSetupElements, state) => {
let preMeasureViewportOverflowState; let preMeasureViewportOverflowState;
if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarStyling) { if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarStyling) {
conditionalClass(_viewport, classNameViewportScrollbarStyling, !showNativeOverlaidScrollbars); _viewportAddRemoveClass(classNameViewportScrollbarStyling, dataValueHostViewportScrollbarStyling, !showNativeOverlaidScrollbars);
} }
if (adjustFlexboxGlue) { if (adjustFlexboxGlue) {
@@ -1503,7 +1534,7 @@ const createOverflowUpdate = (structureSetupElements, state) => {
if (_sizeChanged || _paddingStyleChanged || _contentMutation || _directionChanged || showNativeOverlaidScrollbarsChanged) { if (_sizeChanged || _paddingStyleChanged || _contentMutation || _directionChanged || showNativeOverlaidScrollbarsChanged) {
if (overflowVisible) { if (overflowVisible) {
removeClass(_viewport, classNameOverflowVisible); _viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible, false);
} }
const [redoViewportArrange, undoViewportArrangeOverflowState] = undoViewportArrange(showNativeOverlaidScrollbars, _directionIsRTL, preMeasureViewportOverflowState); const [redoViewportArrange, undoViewportArrangeOverflowState] = undoViewportArrange(showNativeOverlaidScrollbars, _directionIsRTL, preMeasureViewportOverflowState);
@@ -1548,18 +1579,26 @@ 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);
hideNativeScrollbars(viewportOverflowState, _directionIsRTL, viewportArranged, viewportStyle);
if (!_viewportIsTarget) {
hideNativeScrollbars(viewportOverflowState, _directionIsRTL, viewportArranged, viewportStyle);
}
if (adjustFlexboxGlue) { if (adjustFlexboxGlue) {
fixFlexboxGlue(viewportOverflowState, _heightIntrinsic); fixFlexboxGlue(viewportOverflowState, _heightIntrinsic);
} }
style(_viewport, viewportStyle); if (_viewportIsTarget) {
attr(_host, dataAttributeHostOverflowX, viewportStyle.overflowX);
attr(_host, dataAttributeHostOverflowY, viewportStyle.overflowY);
} else {
style(_viewport, viewportStyle);
}
} }
attr(_host, dataAttributeHost, removeClipping ? 'overflowVisible' : ''); attrClass(_host, dataAttributeHost, dataValueHostOverflowVisible, removeClipping);
conditionalClass(_padding, classNameOverflowVisible, removeClipping); conditionalClass(_padding, classNameOverflowVisible, removeClipping);
conditionalClass(_viewport, classNameOverflowVisible, overflowVisible); !_viewportIsTarget && conditionalClass(_viewport, classNameOverflowVisible, overflowVisible);
const [overflowStyle, overflowStyleChanged] = updateOverflowStyleCache(getViewportOverflowState(showNativeOverlaidScrollbars)._overflowStyle); const [overflowStyle, overflowStyleChanged] = updateOverflowStyleCache(getViewportOverflowState(showNativeOverlaidScrollbars)._overflowStyle);
setState({ setState({
_overflowStyle: overflowStyle, _overflowStyle: overflowStyle,
@@ -1910,7 +1949,6 @@ const createDOMObserver = (target, isContentObserver, callback, options) => {
_eventContentChange, _eventContentChange,
_nestedTargetSelector, _nestedTargetSelector,
_ignoreTargetChange, _ignoreTargetChange,
_ignoreNestedTargetChange,
_ignoreContentChange _ignoreContentChange
} = options || {}; } = options || {};
const [destroyEventContentChange, updateEventContentChangeElements] = createEventContentChange(target, debounce(() => { const [destroyEventContentChange, updateEventContentChangeElements] = createEventContentChange(target, debounce(() => {
@@ -1926,7 +1964,7 @@ const createDOMObserver = (target, isContentObserver, callback, options) => {
const observedAttributes = finalAttributes.concat(finalStyleChangingAttributes); const observedAttributes = finalAttributes.concat(finalStyleChangingAttributes);
const observerCallback = mutations => { const observerCallback = mutations => {
const ignoreTargetChange = (isContentObserver ? _ignoreNestedTargetChange : _ignoreTargetChange) || noop; const ignoreTargetChange = _ignoreTargetChange || noop;
const ignoreContentChange = _ignoreContentChange || noop; const ignoreContentChange = _ignoreContentChange || noop;
const targetChangedAttrs = []; const targetChangedAttrs = [];
const totalAddedNodes = []; const totalAddedNodes = [];
@@ -2002,20 +2040,11 @@ const createDOMObserver = (target, isContentObserver, callback, options) => {
}]; }];
}; };
const ignorePrefix = 'os-'; const hostSelector = `[${dataAttributeHost}]`;
const viewportSelector = `.${classNameViewport}`;
const viewportAttrsFromTarget = ['tabindex']; const viewportAttrsFromTarget = ['tabindex'];
const baseStyleChangingAttrsTextarea = ['wrap', 'cols', 'rows']; const baseStyleChangingAttrsTextarea = ['wrap', 'cols', 'rows'];
const baseStyleChangingAttrs = ['id', 'class', 'style', 'open']; const baseStyleChangingAttrs = ['id', 'class', 'style', 'open'];
const ignoreTargetChange = (target, attrName, oldValue, newValue) => {
if (attrName === 'class' && oldValue && newValue) {
const diff = diffClass(oldValue, newValue);
return !!diff.find(addedOrRemovedClass => addedOrRemovedClass.indexOf(ignorePrefix) !== 0);
}
return false;
};
const createStructureSetupObservers = (structureSetupElements, state, structureSetupUpdate) => { const createStructureSetupObservers = (structureSetupElements, state, structureSetupUpdate) => {
let debounceTimeout; let debounceTimeout;
let debounceMaxDelay; let debounceMaxDelay;
@@ -2025,12 +2054,34 @@ const createStructureSetupObservers = (structureSetupElements, state, structureS
_host, _host,
_viewport, _viewport,
_content, _content,
_isTextarea _isTextarea,
_viewportIsTarget,
_viewportHasClass,
_viewportAddRemoveClass
} = structureSetupElements; } = structureSetupElements;
const { const {
_nativeScrollbarStyling, _nativeScrollbarStyling,
_flexboxGlue _flexboxGlue
} = getEnvironment(); } = getEnvironment();
const [updateContentSizeCache] = createCache({
_equal: equalWH,
_initialValue: {
w: 0,
h: 0
}
}, () => {
const has = _viewportHasClass(classNameOverflowVisible, dataValueHostOverflowVisible);
has && _viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible);
const contentScroll = scrollSize(_content);
const viewportScroll = scrollSize(_viewport);
const fractional = fractionalSize(_viewport);
has && _viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible, true);
return {
w: viewportScroll.w + contentScroll.w + fractional.w,
h: viewportScroll.h + contentScroll.h + fractional.h
};
});
const contentMutationObserverAttr = _isTextarea ? baseStyleChangingAttrsTextarea : baseStyleChangingAttrs.concat(baseStyleChangingAttrsTextarea); const contentMutationObserverAttr = _isTextarea ? baseStyleChangingAttrsTextarea : baseStyleChangingAttrs.concat(baseStyleChangingAttrsTextarea);
const structureSetupUpdateWithDebouncedAdaptiveUpdateHints = debounce(structureSetupUpdate, { const structureSetupUpdateWithDebouncedAdaptiveUpdateHints = debounce(structureSetupUpdate, {
_timeout: () => debounceTimeout, _timeout: () => debounceTimeout,
@@ -2094,10 +2145,14 @@ const createStructureSetupObservers = (structureSetupElements, state, structureS
}; };
const onContentMutation = contentChangedTroughEvent => { const onContentMutation = contentChangedTroughEvent => {
const [, contentSizeChanged] = updateContentSizeCache();
const updateFn = contentChangedTroughEvent ? structureSetupUpdate : structureSetupUpdateWithDebouncedAdaptiveUpdateHints; const updateFn = contentChangedTroughEvent ? structureSetupUpdate : structureSetupUpdateWithDebouncedAdaptiveUpdateHints;
updateFn({
_contentMutation: true if (contentSizeChanged) {
}); updateFn({
_contentMutation: true
});
}
}; };
const onHostMutation = (targetChangedAttrs, targetStyleChanged) => { const onHostMutation = (targetChangedAttrs, targetStyleChanged) => {
@@ -2105,28 +2160,34 @@ const createStructureSetupObservers = (structureSetupElements, state, structureS
structureSetupUpdateWithDebouncedAdaptiveUpdateHints({ structureSetupUpdateWithDebouncedAdaptiveUpdateHints({
_hostMutation: true _hostMutation: true
}); });
} else { } else if (!_viewportIsTarget) {
updateViewportAttrsFromHost(targetChangedAttrs); updateViewportAttrsFromHost(targetChangedAttrs);
} }
}; };
const destroyTrinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged); const destroyTrinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
const destroySizeObserver = createSizeObserver(_host, onSizeChanged, { const destroySizeObserver = !_viewportIsTarget && createSizeObserver(_host, onSizeChanged, {
_appear: true, _appear: true,
_direction: !_nativeScrollbarStyling _direction: !_nativeScrollbarStyling
}); });
const [destroyHostMutationObserver] = createDOMObserver(_host, false, onHostMutation, { const [destroyHostMutationObserver] = createDOMObserver(_host, false, onHostMutation, {
_styleChangingAttributes: baseStyleChangingAttrs, _styleChangingAttributes: baseStyleChangingAttrs,
_attributes: baseStyleChangingAttrs.concat(viewportAttrsFromTarget), _attributes: baseStyleChangingAttrs.concat(viewportAttrsFromTarget)
_ignoreTargetChange: ignoreTargetChange
}); });
const viewportIsTargetResizeObserver = _viewportIsTarget && new ResizeObserverConstructor(onSizeChanged.bind(0, {
_sizeChanged: true
}));
viewportIsTargetResizeObserver && viewportIsTargetResizeObserver.observe(_host);
updateViewportAttrsFromHost(); updateViewportAttrsFromHost();
return [checkOption => { return [checkOption => {
const [elementEvents, elementEventsChanged] = checkOption('updating.elementEvents'); const [ignoreMutation] = checkOption('updating.ignoreMutation');
const [attributes, attributesChanged] = checkOption('updating.attributes'); const [attributes, attributesChanged] = checkOption('updating.attributes');
const [elementEvents, elementEventsChanged] = checkOption('updating.elementEvents');
const [debounceValue, debounceChanged] = checkOption('updating.debounce'); const [debounceValue, debounceChanged] = checkOption('updating.debounce');
const updateContentMutationObserver = elementEventsChanged || attributesChanged; const updateContentMutationObserver = elementEventsChanged || attributesChanged;
const ignoreMutationFromOptions = mutation => isFunction(ignoreMutation) && ignoreMutation(mutation);
if (updateContentMutationObserver) { if (updateContentMutationObserver) {
if (contentMutationObserver) { if (contentMutationObserver) {
contentMutationObserver[1](); contentMutationObserver[1]();
@@ -2137,7 +2198,15 @@ const createStructureSetupObservers = (structureSetupElements, state, structureS
_styleChangingAttributes: contentMutationObserverAttr.concat(attributes || []), _styleChangingAttributes: contentMutationObserverAttr.concat(attributes || []),
_attributes: contentMutationObserverAttr.concat(attributes || []), _attributes: contentMutationObserverAttr.concat(attributes || []),
_eventContentChange: elementEvents, _eventContentChange: elementEvents,
_ignoreNestedTargetChange: ignoreTargetChange _nestedTargetSelector: hostSelector,
_ignoreContentChange: (mutation, isNestedTarget) => {
const {
target,
attributeName
} = mutation;
const ignore = !isNestedTarget && attributeName ? liesBetween(target, hostSelector, viewportSelector) : false;
return ignore || !!ignoreMutationFromOptions(mutation);
}
}); });
} }
@@ -2160,7 +2229,8 @@ const createStructureSetupObservers = (structureSetupElements, state, structureS
}, () => { }, () => {
contentMutationObserver && contentMutationObserver[0](); contentMutationObserver && contentMutationObserver[0]();
destroyTrinsicObserver && destroyTrinsicObserver(); destroyTrinsicObserver && destroyTrinsicObserver();
destroySizeObserver(); destroySizeObserver && destroySizeObserver();
viewportIsTargetResizeObserver && viewportIsTargetResizeObserver.disconnect();
destroyHostMutationObserver(); destroyHostMutationObserver();
}]; }];
}; };
@@ -2335,18 +2405,16 @@ const optionsTemplateTypes = ['boolean', 'number', 'string', 'array', 'object',
const numberAllowedValues = optionsTemplateTypes.number; const numberAllowedValues = optionsTemplateTypes.number;
const booleanAllowedValues = optionsTemplateTypes.boolean; const booleanAllowedValues = optionsTemplateTypes.boolean;
const arrayNullValues = [optionsTemplateTypes.array, optionsTemplateTypes.null]; const arrayNullValues = [optionsTemplateTypes.array, optionsTemplateTypes.null];
const stringArrayNullAllowedValues = [optionsTemplateTypes.string, optionsTemplateTypes.array, optionsTemplateTypes.null];
const resizeAllowedValues = 'none both horizontal vertical';
const overflowAllowedValues = 'hidden scroll visible visible-hidden'; const overflowAllowedValues = 'hidden scroll visible visible-hidden';
const scrollbarsVisibilityAllowedValues = 'visible hidden auto'; const scrollbarsVisibilityAllowedValues = 'visible hidden auto';
const scrollbarsAutoHideAllowedValues = 'never scroll leavemove'; const scrollbarsAutoHideAllowedValues = 'never scroll leavemove';
({ ({
resize: resizeAllowedValues,
paddingAbsolute: booleanAllowedValues, paddingAbsolute: booleanAllowedValues,
updating: { updating: {
elementEvents: arrayNullValues, elementEvents: arrayNullValues,
attributes: arrayNullValues, attributes: arrayNullValues,
debounce: [optionsTemplateTypes.number, optionsTemplateTypes.array, optionsTemplateTypes.null] debounce: [optionsTemplateTypes.number, optionsTemplateTypes.array, optionsTemplateTypes.null],
ignoreMutation: [optionsTemplateTypes.function, optionsTemplateTypes.null]
}, },
overflow: { overflow: {
x: overflowAllowedValues, x: overflowAllowedValues,
@@ -2360,17 +2428,9 @@ const scrollbarsAutoHideAllowedValues = 'never scroll leavemove';
clickScroll: booleanAllowedValues, clickScroll: booleanAllowedValues,
touch: booleanAllowedValues touch: booleanAllowedValues
}, },
textarea: {
dynWidth: booleanAllowedValues,
dynHeight: booleanAllowedValues,
inheritedAttrs: stringArrayNullAllowedValues
},
nativeScrollbarsOverlaid: { nativeScrollbarsOverlaid: {
show: booleanAllowedValues, show: booleanAllowedValues,
initialize: booleanAllowedValues initialize: booleanAllowedValues
},
callbacks: {
onUpdated: [optionsTemplateTypes.function, optionsTemplateTypes.null]
} }
}); });
const optionsValidationPluginName = '__osOptionsValidationPlugin'; const optionsValidationPluginName = '__osOptionsValidationPlugin';
@@ -2469,7 +2529,8 @@ const OverlayScrollbars = (target, options, eventListeners) => {
on: addEvent, on: addEvent,
off: removeEvent, off: removeEvent,
state: () => {
state() {
const { const {
_overflowAmount, _overflowAmount,
_overflowStyle, _overflowStyle,
@@ -2486,6 +2547,23 @@ const OverlayScrollbars = (target, options, eventListeners) => {
}); });
}, },
elements() {
const {
_target,
_host,
_padding,
_viewport,
_content
} = structureState._elements;
return assignDeep({}, {
target: _target,
host: _host,
padding: _padding || _viewport,
viewport: _viewport,
content: _content || _viewport
});
},
update(force) { update(force) {
update({}, force); update({}, force);
}, },
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+205 -118
View File
@@ -218,6 +218,17 @@
elm && elm.setAttribute(attrName, value); elm && elm.setAttribute(attrName, value);
} }
var attrClass = function attrClass(elm, attrName, value, add) {
var currValues = attr(elm, attrName) || '';
var currValuesSet = new Set(currValues.split(' '));
currValuesSet[add ? 'add' : 'delete'](value);
attr(elm, attrName, from(currValuesSet).join(' ').trim());
};
var hasAttrClass = function hasAttrClass(elm, attrName, value) {
var currValues = attr(elm, attrName) || '';
var currValuesSet = new Set(currValues.split(' '));
return currValuesSet.has(value);
};
var removeAttr = function removeAttr(elm, attrName) { var removeAttr = function removeAttr(elm, attrName) {
elm && elm.removeAttribute(attrName); elm && elm.removeAttribute(attrName);
}; };
@@ -236,6 +247,11 @@
return rootElm ? push(arr, rootElm.querySelectorAll(selector)) : arr; return rootElm ? push(arr, rootElm.querySelectorAll(selector)) : arr;
}; };
var findFirst = function findFirst(selector, elm) {
var rootElm = elm ? isElement(elm) ? elm : null : document;
return rootElm ? rootElm.querySelector(selector) : null;
};
var is = function is(elm, selector) { var is = function is(elm, selector) {
if (isElement(elm)) { if (isElement(elm)) {
var fn = elmPrototype.matches || elmPrototype.msMatchesSelector; var fn = elmPrototype.matches || elmPrototype.msMatchesSelector;
@@ -253,6 +269,32 @@
return elm ? elm.parentElement : null; return elm ? elm.parentElement : null;
}; };
var closest = function closest(elm, selector) {
if (isElement(elm)) {
var closestFn = elmPrototype.closest;
if (closestFn) {
return closestFn.call(elm, selector);
}
do {
if (is(elm, selector)) {
return elm;
}
elm = parent(elm);
} while (elm);
}
return null;
};
var liesBetween = function liesBetween(elm, highBoundarySelector, deepBoundarySelector) {
var closestHighBoundaryElm = elm && closest(elm, highBoundarySelector);
var closestDeepBoundaryElm = elm && findFirst(deepBoundarySelector, closestHighBoundaryElm);
return closestHighBoundaryElm && closestDeepBoundaryElm ? closestHighBoundaryElm === elm || closestDeepBoundaryElm === elm || closest(closest(elm, deepBoundarySelector), highBoundarySelector) !== closestHighBoundaryElm : false;
};
var before = function before(parentElm, preferredAnchor, insertedElms) { var before = function before(parentElm, preferredAnchor, insertedElms) {
if (insertedElms) { if (insertedElms) {
var anchor = preferredAnchor; var anchor = preferredAnchor;
@@ -386,7 +428,7 @@
var i = 0; var i = 0;
var result = false; var result = false;
if (elm && isString(className)) { if (elm && className && isString(className)) {
var classes = className.match(rnothtmlwhite) || []; var classes = className.match(rnothtmlwhite) || [];
result = classes.length > 0; result = classes.length > 0;
@@ -397,6 +439,12 @@
return result; return result;
}; };
var hasClass = function hasClass(elm, className) {
return classListAction(elm, className, function (classList, clazz) {
return classList.contains(clazz);
});
};
var removeClass = function removeClass(elm, className) { var removeClass = function removeClass(elm, className) {
classListAction(elm, className, function (classList, clazz) { classListAction(elm, className, function (classList, clazz) {
return classList.remove(clazz); return classList.remove(clazz);
@@ -408,22 +456,6 @@
}); });
return removeClass.bind(0, elm, className); return removeClass.bind(0, elm, className);
}; };
var diffClass = function diffClass(classNameA, classNameB) {
var classNameASplit = classNameA && classNameA.split(' ');
var classNameBSplit = classNameB && classNameB.split(' ');
var tempObj = {};
each(classNameASplit, function (className) {
tempObj[className] = 1;
});
each(classNameBSplit, function (className) {
if (tempObj[className]) {
delete tempObj[className];
} else {
tempObj[className] = 1;
}
});
return keys(tempObj);
};
var equal = function equal(a, b, props, propMutation) { var equal = function equal(a, b, props, propMutation) {
if (a && b) { if (a && b) {
@@ -774,6 +806,10 @@
var classNameEnvironmentFlexboxGlue = classNameEnvironment + "-flexbox-glue"; var classNameEnvironmentFlexboxGlue = classNameEnvironment + "-flexbox-glue";
var classNameEnvironmentFlexboxGlueMax = classNameEnvironmentFlexboxGlue + "-max"; var classNameEnvironmentFlexboxGlueMax = classNameEnvironmentFlexboxGlue + "-max";
var dataAttributeHost = 'data-overlayscrollbars'; var dataAttributeHost = 'data-overlayscrollbars';
var dataAttributeHostOverflowX = dataAttributeHost + "-overflow-x";
var dataAttributeHostOverflowY = dataAttributeHost + "-overflow-y";
var dataValueHostOverflowVisible = 'overflowVisible';
var dataValueHostViewportScrollbarStyling = 'viewportStyled';
var classNamePadding = 'os-padding'; var classNamePadding = 'os-padding';
var classNameViewport = 'os-viewport'; var classNameViewport = 'os-viewport';
var classNameViewportArrange = classNameViewport + "-arrange"; var classNameViewportArrange = classNameViewport + "-arrange";
@@ -804,17 +840,21 @@
}; };
var defaultOptions = { var defaultOptions = {
resize: 'none',
paddingAbsolute: false, paddingAbsolute: false,
updating: { updating: {
elementEvents: [['img', 'load']], elementEvents: [['img', 'load']],
debounce: [0, 33],
attributes: null, attributes: null,
debounce: [0, 33] ignoreMutation: null
}, },
overflow: { overflow: {
x: 'scroll', x: 'scroll',
y: 'scroll' y: 'scroll'
}, },
nativeScrollbarsOverlaid: {
show: false,
initialize: false
},
scrollbars: { scrollbars: {
visibility: 'auto', visibility: 'auto',
autoHide: 'never', autoHide: 'never',
@@ -822,18 +862,6 @@
dragScroll: true, dragScroll: true,
clickScroll: false, clickScroll: false,
touch: true touch: true
},
textarea: {
dynWidth: false,
dynHeight: false,
inheritedAttrs: ['style', 'class']
},
nativeScrollbarsOverlaid: {
show: false,
initialize: false
},
callbacks: {
onUpdated: null
} }
}; };
var getOptionsDiff = function getOptionsDiff(currOptions, newOptions) { var getOptionsDiff = function getOptionsDiff(currOptions, newOptions) {
@@ -1085,8 +1113,8 @@
return result === true ? createDiv() : result; return result === true ? createDiv() : result;
}; };
var addDataAttrHost = function addDataAttrHost(elm) { var addDataAttrHost = function addDataAttrHost(elm, value) {
attr(elm, dataAttributeHost, ''); attr(elm, dataAttributeHost, value || '');
return removeAttr.bind(0, elm, dataAttributeHost); return removeAttr.bind(0, elm, dataAttributeHost);
}; };
@@ -1109,20 +1137,32 @@
var ownerDocument = targetElement.ownerDocument; var ownerDocument = targetElement.ownerDocument;
var bodyElm = ownerDocument.body; var bodyElm = ownerDocument.body;
var wnd = ownerDocument.defaultView; var wnd = ownerDocument.defaultView;
var singleElmSupport = !!ResizeObserverConstructor && _nativeScrollbarStyling;
var potentialViewportElement = staticCreationFromStrategy(targetElement, targetStructureInitialization.viewport, viewportInitializationStrategy);
var potentiallySingleElm = potentialViewportElement === targetElement;
var viewportIsTarget = singleElmSupport && potentiallySingleElm;
var viewportElement = potentiallySingleElm && !viewportIsTarget ? staticCreationFromStrategy(targetElement) : potentialViewportElement;
var evaluatedTargetObj = { var evaluatedTargetObj = {
_target: targetElement, _target: targetElement,
_host: isTextarea ? staticCreationFromStrategy(targetElement, targetStructureInitialization.host, hostInitializationStrategy) : targetElement, _host: isTextarea ? staticCreationFromStrategy(targetElement, targetStructureInitialization.host, hostInitializationStrategy) : targetElement,
_viewport: staticCreationFromStrategy(targetElement, targetStructureInitialization.viewport, viewportInitializationStrategy), _viewport: viewportElement,
_padding: dynamicCreationFromStrategy(targetElement, targetStructureInitialization.padding, paddingInitializationStrategy), _padding: dynamicCreationFromStrategy(targetElement, targetStructureInitialization.padding, paddingInitializationStrategy),
_content: dynamicCreationFromStrategy(targetElement, targetStructureInitialization.content, contentInitializationStrategy), _content: dynamicCreationFromStrategy(targetElement, targetStructureInitialization.content, contentInitializationStrategy),
_viewportArrange: createUniqueViewportArrangeElement(), _viewportArrange: !viewportIsTarget && createUniqueViewportArrangeElement(),
_windowElm: wnd, _windowElm: wnd,
_documentElm: ownerDocument, _documentElm: ownerDocument,
_htmlElm: parent(bodyElm), _htmlElm: parent(bodyElm),
_bodyElm: bodyElm, _bodyElm: bodyElm,
_isTextarea: isTextarea, _isTextarea: isTextarea,
_isBody: isBody, _isBody: isBody,
_targetIsElm: targetIsElm _targetIsElm: targetIsElm,
_viewportIsTarget: viewportIsTarget,
_viewportHasClass: function _viewportHasClass(className, attributeClassName) {
return viewportIsTarget ? hasAttrClass(viewportElement, dataAttributeHost, attributeClassName) : hasClass(viewportElement, className);
},
_viewportAddRemoveClass: function _viewportAddRemoveClass(className, attributeClassName, add) {
return viewportIsTarget ? attrClass(viewportElement, dataAttributeHost, attributeClassName, add) : (add ? addClass : removeClass)(viewportElement, className);
}
}; };
var generatedElements = keys(evaluatedTargetObj).reduce(function (arr, key) { var generatedElements = keys(evaluatedTargetObj).reduce(function (arr, key) {
var value = evaluatedTargetObj[key]; var value = evaluatedTargetObj[key];
@@ -1145,9 +1185,9 @@
return elementIsGenerated(elm) === false; return elementIsGenerated(elm) === false;
})); }));
var contentSlot = _content || _viewport; var contentSlot = _content || _viewport;
var removeHostDataAttr = addDataAttrHost(_host); var removeHostDataAttr = addDataAttrHost(_host, viewportIsTarget ? 'viewport' : 'host');
var removePaddingClass = addClass(_padding, classNamePadding); var removePaddingClass = addClass(_padding, classNamePadding);
var removeViewportClass = addClass(_viewport, classNameViewport); var removeViewportClass = addClass(_viewport, !viewportIsTarget && classNameViewport);
var removeContentClass = addClass(_content, classNameContent); var removeContentClass = addClass(_content, classNameContent);
if (isTextareaHostGenerated) { if (isTextareaHostGenerated) {
@@ -1160,34 +1200,31 @@
appendChildren(contentSlot, targetContents); appendChildren(contentSlot, targetContents);
appendChildren(_host, _padding); appendChildren(_host, _padding);
appendChildren(_padding || _host, _viewport); appendChildren(_padding || _host, !viewportIsTarget && _viewport);
appendChildren(_viewport, _content); appendChildren(_viewport, _content);
push(destroyFns, function () { push(destroyFns, function () {
if (targetIsElm) { removeHostDataAttr();
appendChildren(_host, contents(contentSlot)); removeAttr(_viewport, dataAttributeHostOverflowX);
removeElements(_padding || _viewport); removeAttr(_viewport, dataAttributeHostOverflowY);
removeHostDataAttr();
} else {
if (elementIsGenerated(_content)) {
unwrap(_content);
}
if (elementIsGenerated(_viewport)) { if (elementIsGenerated(_content)) {
unwrap(_viewport); unwrap(_content);
}
if (elementIsGenerated(_padding)) {
unwrap(_padding);
}
removeHostDataAttr();
removePaddingClass();
removeViewportClass();
removeContentClass();
} }
if (elementIsGenerated(_viewport)) {
unwrap(_viewport);
}
if (elementIsGenerated(_padding)) {
unwrap(_padding);
}
removePaddingClass();
removeViewportClass();
removeContentClass();
}); });
if (_nativeScrollbarStyling) { if (_nativeScrollbarStyling && !viewportIsTarget) {
push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling)); push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling));
} }
@@ -1214,8 +1251,7 @@
if (heightIntrinsicChanged) { if (heightIntrinsicChanged) {
style(_content, { style(_content, {
height: _heightIntrinsic ? '' : '100%', height: _heightIntrinsic ? '' : '100%'
display: _heightIntrinsic ? '' : 'inline'
}); });
} }
@@ -1231,7 +1267,8 @@
setState = state[1]; setState = state[1];
var _host = structureSetupElements._host, var _host = structureSetupElements._host,
_padding = structureSetupElements._padding, _padding = structureSetupElements._padding,
_viewport = structureSetupElements._viewport; _viewport = structureSetupElements._viewport,
_isSingleElm = structureSetupElements._viewportIsTarget;
var _createCache = createCache({ var _createCache = createCache({
_equal: equalTRBL, _equal: equalTRBL,
@@ -1269,7 +1306,7 @@
paddingChanged = _updatePaddingCache[1]; paddingChanged = _updatePaddingCache[1];
} }
var paddingStyleChanged = paddingAbsoluteChanged || _directionChanged || paddingChanged; var paddingStyleChanged = !_isSingleElm && (paddingAbsoluteChanged || _directionChanged || paddingChanged);
if (paddingStyleChanged) { if (paddingStyleChanged) {
var paddingRelative = !paddingAbsolute || !_padding && !_nativeScrollbarStyling; var paddingRelative = !paddingAbsolute || !_padding && !_nativeScrollbarStyling;
@@ -1336,8 +1373,8 @@
}; };
}; };
var conditionalClass = function conditionalClass(elm, classNames, condition) { var conditionalClass = function conditionalClass(elm, classNames, add) {
return condition ? addClass(elm, classNames) : removeClass(elm, classNames); return add ? addClass(elm, classNames) : removeClass(elm, classNames);
}; };
var overflowIsVisible = function overflowIsVisible(overflowBehavior) { var overflowIsVisible = function overflowIsVisible(overflowBehavior) {
@@ -1350,7 +1387,9 @@
var _host = structureSetupElements._host, var _host = structureSetupElements._host,
_padding = structureSetupElements._padding, _padding = structureSetupElements._padding,
_viewport = structureSetupElements._viewport, _viewport = structureSetupElements._viewport,
_viewportArrange = structureSetupElements._viewportArrange; _viewportArrange = structureSetupElements._viewportArrange,
_viewportIsTarget = structureSetupElements._viewportIsTarget,
_viewportAddRemoveClass = structureSetupElements._viewportAddRemoveClass;
var _getEnvironment = getEnvironment(), var _getEnvironment = getEnvironment(),
_nativeScrollbarSize = _getEnvironment._nativeScrollbarSize, _nativeScrollbarSize = _getEnvironment._nativeScrollbarSize,
@@ -1358,7 +1397,7 @@
_nativeScrollbarStyling = _getEnvironment._nativeScrollbarStyling, _nativeScrollbarStyling = _getEnvironment._nativeScrollbarStyling,
_nativeScrollbarIsOverlaid = _getEnvironment._nativeScrollbarIsOverlaid; _nativeScrollbarIsOverlaid = _getEnvironment._nativeScrollbarIsOverlaid;
var doViewportArrange = !_nativeScrollbarStyling && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y); var doViewportArrange = !_viewportIsTarget && !_nativeScrollbarStyling && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y);
var _createCache = createCache(whCacheOptions, fractionalSize.bind(0, _host)), var _createCache = createCache(whCacheOptions, fractionalSize.bind(0, _host)),
updateSizeFraction = _createCache[0], updateSizeFraction = _createCache[0],
@@ -1603,7 +1642,7 @@
overflowChanged = _checkOption2[1]; overflowChanged = _checkOption2[1];
var showNativeOverlaidScrollbars = showNativeOverlaidScrollbarsOption && _nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y; var showNativeOverlaidScrollbars = showNativeOverlaidScrollbarsOption && _nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y;
var adjustFlexboxGlue = !_flexboxGlue && (_sizeChanged || _contentMutation || _hostMutation || showNativeOverlaidScrollbarsChanged || _heightIntrinsicChanged); var adjustFlexboxGlue = !_viewportIsTarget && !_flexboxGlue && (_sizeChanged || _contentMutation || _hostMutation || showNativeOverlaidScrollbarsChanged || _heightIntrinsicChanged);
var overflowXVisible = overflowIsVisible(overflow.x); var overflowXVisible = overflowIsVisible(overflow.x);
var overflowYVisible = overflowIsVisible(overflow.y); var overflowYVisible = overflowIsVisible(overflow.y);
var overflowVisible = overflowXVisible || overflowYVisible; var overflowVisible = overflowXVisible || overflowYVisible;
@@ -1613,7 +1652,7 @@
var preMeasureViewportOverflowState; var preMeasureViewportOverflowState;
if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarStyling) { if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarStyling) {
conditionalClass(_viewport, classNameViewportScrollbarStyling, !showNativeOverlaidScrollbars); _viewportAddRemoveClass(classNameViewportScrollbarStyling, dataValueHostViewportScrollbarStyling, !showNativeOverlaidScrollbars);
} }
if (adjustFlexboxGlue) { if (adjustFlexboxGlue) {
@@ -1623,7 +1662,7 @@
if (_sizeChanged || _paddingStyleChanged || _contentMutation || _directionChanged || showNativeOverlaidScrollbarsChanged) { if (_sizeChanged || _paddingStyleChanged || _contentMutation || _directionChanged || showNativeOverlaidScrollbarsChanged) {
if (overflowVisible) { if (overflowVisible) {
removeClass(_viewport, classNameOverflowVisible); _viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible, false);
} }
var _undoViewportArrange = undoViewportArrange(showNativeOverlaidScrollbars, _directionIsRTL, preMeasureViewportOverflowState), var _undoViewportArrange = undoViewportArrange(showNativeOverlaidScrollbars, _directionIsRTL, preMeasureViewportOverflowState),
@@ -1683,18 +1722,26 @@
}; };
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);
hideNativeScrollbars(viewportOverflowState, _directionIsRTL, viewportArranged, viewportStyle);
if (!_viewportIsTarget) {
hideNativeScrollbars(viewportOverflowState, _directionIsRTL, viewportArranged, viewportStyle);
}
if (adjustFlexboxGlue) { if (adjustFlexboxGlue) {
fixFlexboxGlue(viewportOverflowState, _heightIntrinsic); fixFlexboxGlue(viewportOverflowState, _heightIntrinsic);
} }
style(_viewport, viewportStyle); if (_viewportIsTarget) {
attr(_host, dataAttributeHostOverflowX, viewportStyle.overflowX);
attr(_host, dataAttributeHostOverflowY, viewportStyle.overflowY);
} else {
style(_viewport, viewportStyle);
}
} }
attr(_host, dataAttributeHost, removeClipping ? 'overflowVisible' : ''); attrClass(_host, dataAttributeHost, dataValueHostOverflowVisible, removeClipping);
conditionalClass(_padding, classNameOverflowVisible, removeClipping); conditionalClass(_padding, classNameOverflowVisible, removeClipping);
conditionalClass(_viewport, classNameOverflowVisible, overflowVisible); !_viewportIsTarget && conditionalClass(_viewport, classNameOverflowVisible, overflowVisible);
var _updateOverflowStyleC = updateOverflowStyleCache(getViewportOverflowState(showNativeOverlaidScrollbars)._overflowStyle), var _updateOverflowStyleC = updateOverflowStyleCache(getViewportOverflowState(showNativeOverlaidScrollbars)._overflowStyle),
overflowStyle = _updateOverflowStyleC[0], overflowStyle = _updateOverflowStyleC[0],
@@ -2070,7 +2117,6 @@
_eventContentChange = _ref._eventContentChange, _eventContentChange = _ref._eventContentChange,
_nestedTargetSelector = _ref._nestedTargetSelector, _nestedTargetSelector = _ref._nestedTargetSelector,
_ignoreTargetChange = _ref._ignoreTargetChange, _ignoreTargetChange = _ref._ignoreTargetChange,
_ignoreNestedTargetChange = _ref._ignoreNestedTargetChange,
_ignoreContentChange = _ref._ignoreContentChange; _ignoreContentChange = _ref._ignoreContentChange;
var _createEventContentCh = createEventContentChange(target, debounce(function () { var _createEventContentCh = createEventContentChange(target, debounce(function () {
@@ -2089,7 +2135,7 @@
var observedAttributes = finalAttributes.concat(finalStyleChangingAttributes); var observedAttributes = finalAttributes.concat(finalStyleChangingAttributes);
var observerCallback = function observerCallback(mutations) { var observerCallback = function observerCallback(mutations) {
var ignoreTargetChange = (isContentObserver ? _ignoreNestedTargetChange : _ignoreTargetChange) || noop; var ignoreTargetChange = _ignoreTargetChange || noop;
var ignoreContentChange = _ignoreContentChange || noop; var ignoreContentChange = _ignoreContentChange || noop;
var targetChangedAttrs = []; var targetChangedAttrs = [];
var totalAddedNodes = []; var totalAddedNodes = [];
@@ -2165,22 +2211,11 @@
}]; }];
}; };
var ignorePrefix = 'os-'; var hostSelector = "[" + dataAttributeHost + "]";
var viewportSelector = "." + classNameViewport;
var viewportAttrsFromTarget = ['tabindex']; var viewportAttrsFromTarget = ['tabindex'];
var baseStyleChangingAttrsTextarea = ['wrap', 'cols', 'rows']; var baseStyleChangingAttrsTextarea = ['wrap', 'cols', 'rows'];
var baseStyleChangingAttrs = ['id', 'class', 'style', 'open']; var baseStyleChangingAttrs = ['id', 'class', 'style', 'open'];
var ignoreTargetChange = function ignoreTargetChange(target, attrName, oldValue, newValue) {
if (attrName === 'class' && oldValue && newValue) {
var diff = diffClass(oldValue, newValue);
return !!diff.find(function (addedOrRemovedClass) {
return addedOrRemovedClass.indexOf(ignorePrefix) !== 0;
});
}
return false;
};
var createStructureSetupObservers = function createStructureSetupObservers(structureSetupElements, state, structureSetupUpdate) { var createStructureSetupObservers = function createStructureSetupObservers(structureSetupElements, state, structureSetupUpdate) {
var debounceTimeout; var debounceTimeout;
var debounceMaxDelay; var debounceMaxDelay;
@@ -2189,12 +2224,36 @@
var _host = structureSetupElements._host, var _host = structureSetupElements._host,
_viewport = structureSetupElements._viewport, _viewport = structureSetupElements._viewport,
_content = structureSetupElements._content, _content = structureSetupElements._content,
_isTextarea = structureSetupElements._isTextarea; _isTextarea = structureSetupElements._isTextarea,
_viewportIsTarget = structureSetupElements._viewportIsTarget,
_viewportHasClass = structureSetupElements._viewportHasClass,
_viewportAddRemoveClass = structureSetupElements._viewportAddRemoveClass;
var _getEnvironment = getEnvironment(), var _getEnvironment = getEnvironment(),
_nativeScrollbarStyling = _getEnvironment._nativeScrollbarStyling, _nativeScrollbarStyling = _getEnvironment._nativeScrollbarStyling,
_flexboxGlue = _getEnvironment._flexboxGlue; _flexboxGlue = _getEnvironment._flexboxGlue;
var _createCache = createCache({
_equal: equalWH,
_initialValue: {
w: 0,
h: 0
}
}, function () {
var has = _viewportHasClass(classNameOverflowVisible, dataValueHostOverflowVisible);
has && _viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible);
var contentScroll = scrollSize(_content);
var viewportScroll = scrollSize(_viewport);
var fractional = fractionalSize(_viewport);
has && _viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible, true);
return {
w: viewportScroll.w + contentScroll.w + fractional.w,
h: viewportScroll.h + contentScroll.h + fractional.h
};
}),
updateContentSizeCache = _createCache[0];
var contentMutationObserverAttr = _isTextarea ? baseStyleChangingAttrsTextarea : baseStyleChangingAttrs.concat(baseStyleChangingAttrsTextarea); var contentMutationObserverAttr = _isTextarea ? baseStyleChangingAttrsTextarea : baseStyleChangingAttrs.concat(baseStyleChangingAttrsTextarea);
var structureSetupUpdateWithDebouncedAdaptiveUpdateHints = debounce(structureSetupUpdate, { var structureSetupUpdateWithDebouncedAdaptiveUpdateHints = debounce(structureSetupUpdate, {
_timeout: function _timeout() { _timeout: function _timeout() {
@@ -2261,10 +2320,16 @@
}; };
var onContentMutation = function onContentMutation(contentChangedTroughEvent) { var onContentMutation = function onContentMutation(contentChangedTroughEvent) {
var _updateContentSizeCac = updateContentSizeCache(),
contentSizeChanged = _updateContentSizeCac[1];
var updateFn = contentChangedTroughEvent ? structureSetupUpdate : structureSetupUpdateWithDebouncedAdaptiveUpdateHints; var updateFn = contentChangedTroughEvent ? structureSetupUpdate : structureSetupUpdateWithDebouncedAdaptiveUpdateHints;
updateFn({
_contentMutation: true if (contentSizeChanged) {
}); updateFn({
_contentMutation: true
});
}
}; };
var onHostMutation = function onHostMutation(targetChangedAttrs, targetStyleChanged) { var onHostMutation = function onHostMutation(targetChangedAttrs, targetStyleChanged) {
@@ -2272,40 +2337,50 @@
structureSetupUpdateWithDebouncedAdaptiveUpdateHints({ structureSetupUpdateWithDebouncedAdaptiveUpdateHints({
_hostMutation: true _hostMutation: true
}); });
} else { } else if (!_viewportIsTarget) {
updateViewportAttrsFromHost(targetChangedAttrs); updateViewportAttrsFromHost(targetChangedAttrs);
} }
}; };
var destroyTrinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged); var destroyTrinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
var destroySizeObserver = createSizeObserver(_host, onSizeChanged, { var destroySizeObserver = !_viewportIsTarget && createSizeObserver(_host, onSizeChanged, {
_appear: true, _appear: true,
_direction: !_nativeScrollbarStyling _direction: !_nativeScrollbarStyling
}); });
var _createDOMObserver = createDOMObserver(_host, false, onHostMutation, { var _createDOMObserver = createDOMObserver(_host, false, onHostMutation, {
_styleChangingAttributes: baseStyleChangingAttrs, _styleChangingAttributes: baseStyleChangingAttrs,
_attributes: baseStyleChangingAttrs.concat(viewportAttrsFromTarget), _attributes: baseStyleChangingAttrs.concat(viewportAttrsFromTarget)
_ignoreTargetChange: ignoreTargetChange
}), }),
destroyHostMutationObserver = _createDOMObserver[0]; destroyHostMutationObserver = _createDOMObserver[0];
var viewportIsTargetResizeObserver = _viewportIsTarget && new ResizeObserverConstructor(onSizeChanged.bind(0, {
_sizeChanged: true
}));
viewportIsTargetResizeObserver && viewportIsTargetResizeObserver.observe(_host);
updateViewportAttrsFromHost(); updateViewportAttrsFromHost();
return [function (checkOption) { return [function (checkOption) {
var _checkOption = checkOption('updating.elementEvents'), var _checkOption = checkOption('updating.ignoreMutation'),
elementEvents = _checkOption[0], ignoreMutation = _checkOption[0];
elementEventsChanged = _checkOption[1];
var _checkOption2 = checkOption('updating.attributes'), var _checkOption2 = checkOption('updating.attributes'),
attributes = _checkOption2[0], attributes = _checkOption2[0],
attributesChanged = _checkOption2[1]; attributesChanged = _checkOption2[1];
var _checkOption3 = checkOption('updating.debounce'), var _checkOption3 = checkOption('updating.elementEvents'),
debounceValue = _checkOption3[0], elementEvents = _checkOption3[0],
debounceChanged = _checkOption3[1]; elementEventsChanged = _checkOption3[1];
var _checkOption4 = checkOption('updating.debounce'),
debounceValue = _checkOption4[0],
debounceChanged = _checkOption4[1];
var updateContentMutationObserver = elementEventsChanged || attributesChanged; var updateContentMutationObserver = elementEventsChanged || attributesChanged;
var ignoreMutationFromOptions = function ignoreMutationFromOptions(mutation) {
return isFunction(ignoreMutation) && ignoreMutation(mutation);
};
if (updateContentMutationObserver) { if (updateContentMutationObserver) {
if (contentMutationObserver) { if (contentMutationObserver) {
contentMutationObserver[1](); contentMutationObserver[1]();
@@ -2316,7 +2391,13 @@
_styleChangingAttributes: contentMutationObserverAttr.concat(attributes || []), _styleChangingAttributes: contentMutationObserverAttr.concat(attributes || []),
_attributes: contentMutationObserverAttr.concat(attributes || []), _attributes: contentMutationObserverAttr.concat(attributes || []),
_eventContentChange: elementEvents, _eventContentChange: elementEvents,
_ignoreNestedTargetChange: ignoreTargetChange _nestedTargetSelector: hostSelector,
_ignoreContentChange: function _ignoreContentChange(mutation, isNestedTarget) {
var target = mutation.target,
attributeName = mutation.attributeName;
var ignore = !isNestedTarget && attributeName ? liesBetween(target, hostSelector, viewportSelector) : false;
return ignore || !!ignoreMutationFromOptions(mutation);
}
}); });
} }
@@ -2339,7 +2420,8 @@
}, function () { }, function () {
contentMutationObserver && contentMutationObserver[0](); contentMutationObserver && contentMutationObserver[0]();
destroyTrinsicObserver && destroyTrinsicObserver(); destroyTrinsicObserver && destroyTrinsicObserver();
destroySizeObserver(); destroySizeObserver && destroySizeObserver();
viewportIsTargetResizeObserver && viewportIsTargetResizeObserver.disconnect();
destroyHostMutationObserver(); destroyHostMutationObserver();
}]; }];
}; };
@@ -2521,18 +2603,16 @@
var numberAllowedValues = optionsTemplateTypes.number; var numberAllowedValues = optionsTemplateTypes.number;
var booleanAllowedValues = optionsTemplateTypes.boolean; var booleanAllowedValues = optionsTemplateTypes.boolean;
var arrayNullValues = [optionsTemplateTypes.array, optionsTemplateTypes.null]; var arrayNullValues = [optionsTemplateTypes.array, optionsTemplateTypes.null];
var stringArrayNullAllowedValues = [optionsTemplateTypes.string, optionsTemplateTypes.array, optionsTemplateTypes.null];
var resizeAllowedValues = 'none both horizontal vertical';
var overflowAllowedValues = 'hidden scroll visible visible-hidden'; var overflowAllowedValues = 'hidden scroll visible visible-hidden';
var scrollbarsVisibilityAllowedValues = 'visible hidden auto'; var scrollbarsVisibilityAllowedValues = 'visible hidden auto';
var scrollbarsAutoHideAllowedValues = 'never scroll leavemove'; var scrollbarsAutoHideAllowedValues = 'never scroll leavemove';
({ ({
resize: resizeAllowedValues,
paddingAbsolute: booleanAllowedValues, paddingAbsolute: booleanAllowedValues,
updating: { updating: {
elementEvents: arrayNullValues, elementEvents: arrayNullValues,
attributes: arrayNullValues, attributes: arrayNullValues,
debounce: [optionsTemplateTypes.number, optionsTemplateTypes.array, optionsTemplateTypes.null] debounce: [optionsTemplateTypes.number, optionsTemplateTypes.array, optionsTemplateTypes.null],
ignoreMutation: [optionsTemplateTypes.function, optionsTemplateTypes.null]
}, },
overflow: { overflow: {
x: overflowAllowedValues, x: overflowAllowedValues,
@@ -2546,17 +2626,9 @@
clickScroll: booleanAllowedValues, clickScroll: booleanAllowedValues,
touch: booleanAllowedValues touch: booleanAllowedValues
}, },
textarea: {
dynWidth: booleanAllowedValues,
dynHeight: booleanAllowedValues,
inheritedAttrs: stringArrayNullAllowedValues
},
nativeScrollbarsOverlaid: { nativeScrollbarsOverlaid: {
show: booleanAllowedValues, show: booleanAllowedValues,
initialize: booleanAllowedValues initialize: booleanAllowedValues
},
callbacks: {
onUpdated: [optionsTemplateTypes.function, optionsTemplateTypes.null]
} }
}); });
var optionsValidationPluginName = '__osOptionsValidationPlugin'; var optionsValidationPluginName = '__osOptionsValidationPlugin';
@@ -2683,6 +2755,21 @@
paddingAbsolute: _paddingAbsolute paddingAbsolute: _paddingAbsolute
}); });
}, },
elements: function elements() {
var _structureState$_elem = structureState._elements,
_target = _structureState$_elem._target,
_host = _structureState$_elem._host,
_padding = _structureState$_elem._padding,
_viewport = _structureState$_elem._viewport,
_content = _structureState$_elem._content;
return assignDeep({}, {
target: _target,
host: _host,
padding: _padding || _viewport,
viewport: _viewport,
content: _content || _viewport
});
},
update: function update(force) { update: function update(force) {
_update({}, force); _update({}, force);
}, },
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -3,6 +3,10 @@ export const classNameEnvironmentFlexboxGlue = `${classNameEnvironment}-flexbox-
export const classNameEnvironmentFlexboxGlueMax = `${classNameEnvironmentFlexboxGlue}-max`; export const classNameEnvironmentFlexboxGlueMax = `${classNameEnvironmentFlexboxGlue}-max`;
export const dataAttributeHost = 'data-overlayscrollbars'; export const dataAttributeHost = 'data-overlayscrollbars';
export const dataAttributeHostOverflowX = `${dataAttributeHost}-overflow-x`;
export const dataAttributeHostOverflowY = `${dataAttributeHost}-overflow-y`;
export const dataValueHostOverflowVisible = 'overflowVisible';
export const dataValueHostViewportScrollbarStyling = 'viewportStyled';
export const classNamePadding = 'os-padding'; export const classNamePadding = 'os-padding';
export const classNameViewport = 'os-viewport'; export const classNameViewport = 'os-viewport';
export const classNameViewportArrange = `${classNameViewport}-arrange`; export const classNameViewportArrange = `${classNameViewport}-arrange`;
@@ -10,6 +10,7 @@ import {
indexOf, indexOf,
removeElements, removeElements,
removeClass, removeClass,
hasClass,
push, push,
runEach, runEach,
insertBefore, insertBefore,
@@ -18,9 +19,14 @@ import {
isFunction, isFunction,
keys, keys,
removeAttr, removeAttr,
attrClass,
hasAttrClass,
ResizeObserverConstructor,
} from 'support'; } from 'support';
import { import {
dataAttributeHost, dataAttributeHost,
dataAttributeHostOverflowX,
dataAttributeHostOverflowY,
classNamePadding, classNamePadding,
classNameViewport, classNameViewport,
classNameViewportArrange, classNameViewportArrange,
@@ -51,6 +57,9 @@ export interface StructureSetupElementsObj {
_windowElm: Window; _windowElm: Window;
_documentElm: Document; _documentElm: Document;
_targetIsElm: boolean; _targetIsElm: boolean;
_viewportIsTarget: boolean;
_viewportHasClass: (className: string, attributeClassName: string) => boolean;
_viewportAddRemoveClass: (className: string, attributeClassName: string, add?: boolean) => void;
} }
let contentArrangeCounter = 0; let contentArrangeCounter = 0;
@@ -80,8 +89,8 @@ const createUniqueViewportArrangeElement = (): HTMLStyleElement | false => {
const staticCreationFromStrategy = ( const staticCreationFromStrategy = (
target: OSTargetElement, target: OSTargetElement,
initializationValue: HTMLElement | undefined, initializationValue?: HTMLElement | undefined,
strategy: StructureInitializationStrategyStaticElement strategy?: StructureInitializationStrategyStaticElement
): HTMLElement => { ): HTMLElement => {
const result = const result =
initializationValue || initializationValue ||
@@ -104,8 +113,8 @@ const dynamicCreationFromStrategy = (
return result === true ? createDiv() : result; return result === true ? createDiv() : result;
}; };
const addDataAttrHost = (elm: HTMLElement) => { const addDataAttrHost = (elm: HTMLElement, value?: string | false | null | undefined) => {
attr(elm, dataAttributeHost, ''); attr(elm, dataAttributeHost, value || '');
return removeAttr.bind(0, elm, dataAttributeHost); return removeAttr.bind(0, elm, dataAttributeHost);
}; };
@@ -127,6 +136,18 @@ export const createStructureSetupElements = (target: OSTarget): StructureSetupEl
const ownerDocument = targetElement!.ownerDocument; const ownerDocument = targetElement!.ownerDocument;
const bodyElm = ownerDocument.body as HTMLBodyElement; const bodyElm = ownerDocument.body as HTMLBodyElement;
const wnd = ownerDocument.defaultView as Window; const wnd = ownerDocument.defaultView as Window;
const singleElmSupport = !!ResizeObserverConstructor && _nativeScrollbarStyling;
const potentialViewportElement = staticCreationFromStrategy(
targetElement,
targetStructureInitialization.viewport,
viewportInitializationStrategy
);
const potentiallySingleElm = potentialViewportElement === targetElement;
const viewportIsTarget = singleElmSupport && potentiallySingleElm;
const viewportElement =
potentiallySingleElm && !viewportIsTarget
? staticCreationFromStrategy(targetElement)
: potentialViewportElement;
const evaluatedTargetObj: StructureSetupElementsObj = { const evaluatedTargetObj: StructureSetupElementsObj = {
_target: targetElement, _target: targetElement,
_host: isTextarea _host: isTextarea
@@ -136,11 +157,7 @@ export const createStructureSetupElements = (target: OSTarget): StructureSetupEl
hostInitializationStrategy hostInitializationStrategy
) )
: (targetElement as HTMLElement), : (targetElement as HTMLElement),
_viewport: staticCreationFromStrategy( _viewport: viewportElement,
targetElement,
targetStructureInitialization.viewport,
viewportInitializationStrategy
),
_padding: dynamicCreationFromStrategy( _padding: dynamicCreationFromStrategy(
targetElement, targetElement,
targetStructureInitialization.padding, targetStructureInitialization.padding,
@@ -151,7 +168,7 @@ export const createStructureSetupElements = (target: OSTarget): StructureSetupEl
targetStructureInitialization.content, targetStructureInitialization.content,
contentInitializationStrategy contentInitializationStrategy
), ),
_viewportArrange: createUniqueViewportArrangeElement(), _viewportArrange: !viewportIsTarget && createUniqueViewportArrangeElement(),
_windowElm: wnd, _windowElm: wnd,
_documentElm: ownerDocument, _documentElm: ownerDocument,
_htmlElm: parent(bodyElm) as HTMLHtmlElement, _htmlElm: parent(bodyElm) as HTMLHtmlElement,
@@ -159,6 +176,15 @@ export const createStructureSetupElements = (target: OSTarget): StructureSetupEl
_isTextarea: isTextarea, _isTextarea: isTextarea,
_isBody: isBody, _isBody: isBody,
_targetIsElm: targetIsElm, _targetIsElm: targetIsElm,
_viewportIsTarget: viewportIsTarget,
_viewportHasClass: (className: string, attributeClassName: string) =>
viewportIsTarget
? hasAttrClass(viewportElement, dataAttributeHost, attributeClassName)
: hasClass(viewportElement, className),
_viewportAddRemoveClass: (className: string, attributeClassName: string, add?: boolean) =>
viewportIsTarget
? attrClass(viewportElement, dataAttributeHost, attributeClassName, add)
: (add ? addClass : removeClass)(viewportElement, className),
}; };
const generatedElements = keys(evaluatedTargetObj).reduce((arr, key: string) => { const generatedElements = keys(evaluatedTargetObj).reduce((arr, key: string) => {
const value = evaluatedTargetObj[key]; const value = evaluatedTargetObj[key];
@@ -177,9 +203,9 @@ export const createStructureSetupElements = (target: OSTarget): StructureSetupEl
) )
); );
const contentSlot = _content || _viewport; const contentSlot = _content || _viewport;
const removeHostDataAttr = addDataAttrHost(_host); const removeHostDataAttr = addDataAttrHost(_host, viewportIsTarget ? 'viewport' : 'host');
const removePaddingClass = addClass(_padding, classNamePadding); const removePaddingClass = addClass(_padding, classNamePadding);
const removeViewportClass = addClass(_viewport, classNameViewport); const removeViewportClass = addClass(_viewport, !viewportIsTarget && classNameViewport);
const removeContentClass = addClass(_content, classNameContent); const removeContentClass = addClass(_content, classNameContent);
// only insert host for textarea after target if it was generated // only insert host for textarea after target if it was generated
@@ -194,32 +220,29 @@ export const createStructureSetupElements = (target: OSTarget): StructureSetupEl
appendChildren(contentSlot, targetContents); appendChildren(contentSlot, targetContents);
appendChildren(_host, _padding); appendChildren(_host, _padding);
appendChildren(_padding || _host, _viewport); appendChildren(_padding || _host, !viewportIsTarget && _viewport);
appendChildren(_viewport, _content); appendChildren(_viewport, _content);
push(destroyFns, () => { push(destroyFns, () => {
if (targetIsElm) { removeHostDataAttr();
appendChildren(_host, contents(contentSlot)); removeAttr(_viewport, dataAttributeHostOverflowX);
removeElements(_padding || _viewport); removeAttr(_viewport, dataAttributeHostOverflowY);
removeHostDataAttr();
} else { if (elementIsGenerated(_content)) {
if (elementIsGenerated(_content)) { unwrap(_content);
unwrap(_content);
}
if (elementIsGenerated(_viewport)) {
unwrap(_viewport);
}
if (elementIsGenerated(_padding)) {
unwrap(_padding);
}
removeHostDataAttr();
removePaddingClass();
removeViewportClass();
removeContentClass();
} }
if (elementIsGenerated(_viewport)) {
unwrap(_viewport);
}
if (elementIsGenerated(_padding)) {
unwrap(_padding);
}
removePaddingClass();
removeViewportClass();
removeContentClass();
}); });
if (_nativeScrollbarStyling) { if (_nativeScrollbarStyling && !viewportIsTarget) {
push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling)); push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling));
} }
if (_viewportArrange) { if (_viewportArrange) {
@@ -15,13 +15,16 @@ import {
createCache, createCache,
WH, WH,
fractionalSize, fractionalSize,
removeClass,
addClass,
hasClass,
isFunction, isFunction,
ResizeObserverConstructor,
} from 'support'; } from 'support';
import { getEnvironment } from 'environment'; import { getEnvironment } from 'environment';
import { dataAttributeHost, classNameViewport, classNameOverflowVisible } from 'classnames'; import {
dataAttributeHost,
dataValueHostOverflowVisible,
classNameViewport,
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';
@@ -66,22 +69,31 @@ export const createStructureSetupObservers = (
let debounceMaxDelay: number | false | undefined; let debounceMaxDelay: number | false | undefined;
let contentMutationObserver: DOMObserver | undefined; let contentMutationObserver: DOMObserver | undefined;
const [, setState] = state; const [, setState] = state;
const { _host, _viewport, _content, _isTextarea } = structureSetupElements; const {
_host,
_viewport,
_content,
_isTextarea,
_viewportIsTarget,
_viewportHasClass,
_viewportAddRemoveClass,
} = structureSetupElements;
const { _nativeScrollbarStyling, _flexboxGlue } = getEnvironment(); const { _nativeScrollbarStyling, _flexboxGlue } = getEnvironment();
const [updateContentSizeCache] = createCache<WH<number>>( const [updateContentSizeCache] = createCache<WH<number>>(
{ {
_equal: equalWH, _equal: equalWH,
_initialValue: { w: 0, h: 0 }, _initialValue: { w: 0, h: 0 },
}, },
() => { () => {
const has = hasClass(_viewport, classNameOverflowVisible); const has = _viewportHasClass(classNameOverflowVisible, dataValueHostOverflowVisible);
has && removeClass(_viewport, classNameOverflowVisible); has && _viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible);
const contentScroll = scrollSize(_content); const contentScroll = scrollSize(_content);
const viewportScroll = scrollSize(_viewport); const viewportScroll = scrollSize(_viewport);
const fractional = fractionalSize(_viewport); const fractional = fractionalSize(_viewport);
has && addClass(_viewport, classNameOverflowVisible); has && _viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible, true);
return { return {
w: viewportScroll.w + contentScroll.w + fractional.w, w: viewportScroll.w + contentScroll.w + fractional.w,
h: viewportScroll.h + contentScroll.h + fractional.h, h: viewportScroll.h + contentScroll.h + fractional.h,
@@ -162,22 +174,29 @@ export const createStructureSetupObservers = (
structureSetupUpdateWithDebouncedAdaptiveUpdateHints({ structureSetupUpdateWithDebouncedAdaptiveUpdateHints({
_hostMutation: true, _hostMutation: true,
}); });
} else { } else if (!_viewportIsTarget) {
updateViewportAttrsFromHost(targetChangedAttrs); updateViewportAttrsFromHost(targetChangedAttrs);
} }
}; };
const destroyTrinsicObserver = const destroyTrinsicObserver =
(_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged); (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
const destroySizeObserver = createSizeObserver(_host, onSizeChanged, { const destroySizeObserver =
_appear: true, !_viewportIsTarget &&
_direction: !_nativeScrollbarStyling, createSizeObserver(_host, onSizeChanged, {
}); _appear: true,
_direction: !_nativeScrollbarStyling,
});
const [destroyHostMutationObserver] = createDOMObserver(_host, false, onHostMutation, { const [destroyHostMutationObserver] = createDOMObserver(_host, false, onHostMutation, {
_styleChangingAttributes: baseStyleChangingAttrs, _styleChangingAttributes: baseStyleChangingAttrs,
_attributes: baseStyleChangingAttrs.concat(viewportAttrsFromTarget), _attributes: baseStyleChangingAttrs.concat(viewportAttrsFromTarget),
}); });
const viewportIsTargetResizeObserver =
_viewportIsTarget &&
new ResizeObserverConstructor!(onSizeChanged.bind(0, { _sizeChanged: true }));
viewportIsTargetResizeObserver && viewportIsTargetResizeObserver.observe(_host);
updateViewportAttrsFromHost(); updateViewportAttrsFromHost();
return [ return [
@@ -239,7 +258,8 @@ export const createStructureSetupObservers = (
() => { () => {
contentMutationObserver && contentMutationObserver[0](); // destroy contentMutationObserver && contentMutationObserver[0](); // destroy
destroyTrinsicObserver && destroyTrinsicObserver(); destroyTrinsicObserver && destroyTrinsicObserver();
destroySizeObserver(); destroySizeObserver && destroySizeObserver();
viewportIsTargetResizeObserver && viewportIsTargetResizeObserver.disconnect();
destroyHostMutationObserver(); destroyHostMutationObserver();
}, },
]; ];
@@ -14,6 +14,7 @@ import {
noop, noop,
each, each,
equalXY, equalXY,
attrClass,
} from 'support'; } from 'support';
import { getEnvironment } from 'environment'; import { getEnvironment } from 'environment';
import { import {
@@ -21,6 +22,10 @@ import {
classNameViewportScrollbarStyling, classNameViewportScrollbarStyling,
classNameOverflowVisible, classNameOverflowVisible,
dataAttributeHost, dataAttributeHost,
dataAttributeHostOverflowX,
dataAttributeHostOverflowY,
dataValueHostViewportScrollbarStyling,
dataValueHostOverflowVisible,
} from 'classnames'; } from 'classnames';
import type { StyleObject, OverflowStyle } from 'typings'; import type { StyleObject, OverflowStyle } from 'typings';
import type { OverflowBehavior } from 'options'; import type { OverflowBehavior } from 'options';
@@ -71,8 +76,8 @@ const getOverflowAmount = (
const conditionalClass = ( const conditionalClass = (
elm: Element | false | null | undefined, elm: Element | false | null | undefined,
classNames: string, classNames: string,
condition: boolean add: boolean
) => (condition ? addClass(elm, classNames) : removeClass(elm, classNames)); ) => (add ? addClass(elm, classNames) : removeClass(elm, classNames));
const overflowIsVisible = (overflowBehavior: string) => overflowBehavior.indexOf(strVisible) === 0; const overflowIsVisible = (overflowBehavior: string) => overflowBehavior.indexOf(strVisible) === 0;
@@ -86,7 +91,14 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = (
state state
) => { ) => {
const [getState, setState] = state; const [getState, setState] = state;
const { _host, _padding, _viewport, _viewportArrange } = structureSetupElements; const {
_host,
_padding,
_viewport,
_viewportArrange,
_viewportIsTarget,
_viewportAddRemoveClass,
} = structureSetupElements;
const { const {
_nativeScrollbarSize, _nativeScrollbarSize,
_flexboxGlue, _flexboxGlue,
@@ -94,7 +106,9 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = (
_nativeScrollbarIsOverlaid, _nativeScrollbarIsOverlaid,
} = getEnvironment(); } = getEnvironment();
const doViewportArrange = const doViewportArrange =
!_nativeScrollbarStyling && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y); !_viewportIsTarget &&
!_nativeScrollbarStyling &&
(_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y);
const [updateSizeFraction, getCurrentSizeFraction] = createCache<WH<number>>( const [updateSizeFraction, getCurrentSizeFraction] = createCache<WH<number>>(
whCacheOptions, whCacheOptions,
@@ -426,6 +440,7 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = (
_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.x &&
_nativeScrollbarIsOverlaid.y; _nativeScrollbarIsOverlaid.y;
const adjustFlexboxGlue = const adjustFlexboxGlue =
!_viewportIsTarget &&
!_flexboxGlue && !_flexboxGlue &&
(_sizeChanged || (_sizeChanged ||
_contentMutation || _contentMutation ||
@@ -443,7 +458,11 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = (
let preMeasureViewportOverflowState: ViewportOverflowState | undefined; let preMeasureViewportOverflowState: ViewportOverflowState | undefined;
if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarStyling) { if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarStyling) {
conditionalClass(_viewport, classNameViewportScrollbarStyling, !showNativeOverlaidScrollbars); _viewportAddRemoveClass(
classNameViewportScrollbarStyling,
dataValueHostViewportScrollbarStyling,
!showNativeOverlaidScrollbars
);
} }
if (adjustFlexboxGlue) { if (adjustFlexboxGlue) {
@@ -459,7 +478,7 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = (
showNativeOverlaidScrollbarsChanged showNativeOverlaidScrollbarsChanged
) { ) {
if (overflowVisible) { if (overflowVisible) {
removeClass(_viewport, classNameOverflowVisible); _viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible, false);
} }
const [redoViewportArrange, undoViewportArrangeOverflowState] = undoViewportArrange( const [redoViewportArrange, undoViewportArrangeOverflowState] = undoViewportArrange(
@@ -550,18 +569,31 @@ export const createOverflowUpdate: CreateStructureUpdateSegment = (
sizeFraction, sizeFraction,
_directionIsRTL _directionIsRTL
); );
hideNativeScrollbars(viewportOverflowState, _directionIsRTL, viewportArranged, viewportStyle);
if (!_viewportIsTarget) {
hideNativeScrollbars(
viewportOverflowState,
_directionIsRTL,
viewportArranged,
viewportStyle
);
}
if (adjustFlexboxGlue) { if (adjustFlexboxGlue) {
fixFlexboxGlue(viewportOverflowState, _heightIntrinsic); fixFlexboxGlue(viewportOverflowState, _heightIntrinsic);
} }
style(_viewport, viewportStyle); if (_viewportIsTarget) {
attr(_host, dataAttributeHostOverflowX, viewportStyle.overflowX as string);
attr(_host, dataAttributeHostOverflowY, viewportStyle.overflowY as string);
} else {
style(_viewport, viewportStyle);
}
} }
attr(_host, dataAttributeHost, removeClipping ? 'overflowVisible' : ''); attrClass(_host, dataAttributeHost, dataValueHostOverflowVisible, removeClipping);
conditionalClass(_padding, classNameOverflowVisible, removeClipping); conditionalClass(_padding, classNameOverflowVisible, removeClipping);
conditionalClass(_viewport, classNameOverflowVisible, overflowVisible); !_viewportIsTarget && conditionalClass(_viewport, classNameOverflowVisible, overflowVisible);
const [overflowStyle, overflowStyleChanged] = updateOverflowStyleCache( const [overflowStyle, overflowStyleChanged] = updateOverflowStyleCache(
getViewportOverflowState(showNativeOverlaidScrollbars)._overflowStyle getViewportOverflowState(showNativeOverlaidScrollbars)._overflowStyle
@@ -13,7 +13,7 @@ export const createPaddingUpdate: CreateStructureUpdateSegment = (
state state
) => { ) => {
const [getState, setState] = state; const [getState, setState] = state;
const { _host, _padding, _viewport } = structureSetupElements; const { _host, _padding, _viewport, _viewportIsTarget: _isSingleElm } = structureSetupElements;
const [updatePaddingCache, currentPaddingCache] = createCache( const [updatePaddingCache, currentPaddingCache] = createCache(
{ {
_equal: equalTRBL, _equal: equalTRBL,
@@ -34,7 +34,8 @@ export const createPaddingUpdate: CreateStructureUpdateSegment = (
[padding, paddingChanged] = updatePaddingCache(force); [padding, paddingChanged] = updatePaddingCache(force);
} }
const paddingStyleChanged = paddingAbsoluteChanged || _directionChanged || paddingChanged; const paddingStyleChanged =
!_isSingleElm && (paddingAbsoluteChanged || _directionChanged || paddingChanged);
if (paddingStyleChanged) { if (paddingStyleChanged) {
// if there is no padding element and no scrollbar styling, paddingAbsolute isn't supported // if there is no padding element and no scrollbar styling, paddingAbsolute isn't supported
@@ -57,10 +57,13 @@
.os-viewport { .os-viewport {
-ms-overflow-style: scrollbar !important; -ms-overflow-style: scrollbar !important;
} }
[data-overlayscrollbars~='viewportStyled'],
.os-viewport-scrollbar-styled.os-environment, .os-viewport-scrollbar-styled.os-environment,
.os-viewport-scrollbar-styled.os-viewport { .os-viewport-scrollbar-styled.os-viewport {
scrollbar-width: none !important; scrollbar-width: none !important;
} }
[data-overlayscrollbars~='viewportStyled']::-webkit-scrollbar,
[data-overlayscrollbars~='viewportStyled']::-webkit-scrollbar-corner,
.os-viewport-scrollbar-styled.os-environment::-webkit-scrollbar, .os-viewport-scrollbar-styled.os-environment::-webkit-scrollbar,
.os-viewport-scrollbar-styled.os-viewport::-webkit-scrollbar, .os-viewport-scrollbar-styled.os-viewport::-webkit-scrollbar,
.os-viewport-scrollbar-styled.os-environment::-webkit-scrollbar-corner, .os-viewport-scrollbar-styled.os-environment::-webkit-scrollbar-corner,
@@ -72,7 +75,7 @@
background: transparent !important; background: transparent !important;
} }
[data-overlayscrollbars], [data-overlayscrollbars~='host'],
.os-padding { .os-padding {
position: relative; position: relative;
display: flex; display: flex;
@@ -115,6 +118,18 @@
[data-overlayscrollbars~='overflowVisible'] { [data-overlayscrollbars~='overflowVisible'] {
overflow: visible !important; overflow: visible !important;
} }
[data-overlayscrollbars-overflow-x='hidden'] {
overflow-x: hidden !important;
}
[data-overlayscrollbars-overflow-x='scroll'] {
overflow-x: scroll !important;
}
[data-overlayscrollbars-overflow-x='hidden'] {
overflow-y: hidden !important;
}
[data-overlayscrollbars-overflow-y='scroll'] {
overflow-y: scroll !important;
}
.os-padding, .os-padding,
.os-viewport { .os-viewport {
@@ -1,3 +1,4 @@
import { from } from 'support/utils/array';
import { isUndefined } from 'support/utils/types'; import { isUndefined } from 'support/utils/types';
type GetSetPropName = 'scrollLeft' | 'scrollTop' | 'value'; type GetSetPropName = 'scrollLeft' | 'scrollTop' | 'value';
@@ -5,7 +6,7 @@ type GetSetPropName = 'scrollLeft' | 'scrollTop' | 'value';
function getSetProp( function getSetProp(
topLeft: GetSetPropName, topLeft: GetSetPropName,
fallback: number | string, fallback: number | string,
elm: HTMLElement | HTMLInputElement | null, elm: HTMLElement | HTMLInputElement | false | null | undefined,
value?: number | string value?: number | string
): number | string | void { ): number | string | void {
if (isUndefined(value)) { if (isUndefined(value)) {
@@ -21,10 +22,14 @@ function getSetProp(
* @param attrName The attribute name which shall be get or set. * @param attrName The attribute name which shall be get or set.
* @param value The value of the attribute which shall be set. * @param value The value of the attribute which shall be set.
*/ */
export function attr(elm: HTMLElement | null, attrName: string): string | null; export function attr(elm: HTMLElement | false | null | undefined, attrName: string): string | null;
export function attr(elm: HTMLElement | null, attrName: string, value: string): void;
export function attr( export function attr(
elm: HTMLElement | null, elm: HTMLElement | false | null | undefined,
attrName: string,
value: string
): void;
export function attr(
elm: HTMLElement | false | null | undefined,
attrName: string, attrName: string,
value?: string value?: string
): string | null | void { ): string | null | void {
@@ -34,12 +39,35 @@ export function attr(
elm && elm.setAttribute(attrName, value); elm && elm.setAttribute(attrName, value);
} }
export const attrClass = (
elm: HTMLElement | false | null | undefined,
attrName: string,
value: string,
add?: boolean
) => {
const currValues = attr(elm, attrName) || '';
const currValuesSet = new Set(currValues.split(' '));
currValuesSet[add ? 'add' : 'delete'](value);
attr(elm, attrName, from(currValuesSet).join(' ').trim());
};
export const hasAttrClass = (
elm: HTMLElement | false | null | undefined,
attrName: string,
value: string
) => {
const currValues = attr(elm, attrName) || '';
const currValuesSet = new Set(currValues.split(' '));
return currValuesSet.has(value);
};
/** /**
* Removes the given attribute from the given element. * Removes the given attribute from the given element.
* @param elm The element of which the attribute shall be removed. * @param elm The element of which the attribute shall be removed.
* @param attrName The attribute name. * @param attrName The attribute name.
*/ */
export const removeAttr = (elm: Element | null, attrName: string): void => { export const removeAttr = (elm: Element | false | null | undefined, attrName: string): void => {
elm && elm.removeAttribute(attrName); elm && elm.removeAttribute(attrName);
}; };
@@ -48,9 +76,12 @@ export const removeAttr = (elm: Element | null, attrName: string): void => {
* @param elm The element of which the scrollLeft value shall be get or set. * @param elm The element of which the scrollLeft value shall be get or set.
* @param value The scrollLeft value which shall be set. * @param value The scrollLeft value which shall be set.
*/ */
export function scrollLeft(elm: HTMLElement | null): number; export function scrollLeft(elm: HTMLElement | false | null | undefined): number;
export function scrollLeft(elm: HTMLElement | null, value: number): void; export function scrollLeft(elm: HTMLElement | false | null | undefined, value: number): void;
export function scrollLeft(elm: HTMLElement | null, value?: number): number | void { export function scrollLeft(
elm: HTMLElement | false | null | undefined,
value?: number
): number | void {
return getSetProp('scrollLeft', 0, elm, value) as number; return getSetProp('scrollLeft', 0, elm, value) as number;
} }
@@ -59,9 +90,12 @@ export function scrollLeft(elm: HTMLElement | null, value?: number): number | vo
* @param elm The element of which the scrollTop value shall be get or set. * @param elm The element of which the scrollTop value shall be get or set.
* @param value The scrollTop value which shall be set. * @param value The scrollTop value which shall be set.
*/ */
export function scrollTop(elm: HTMLElement | null): number; export function scrollTop(elm: HTMLElement | false | null | undefined): number;
export function scrollTop(elm: HTMLElement | null, value: number): void; export function scrollTop(elm: HTMLElement | false | null | undefined, value: number): void;
export function scrollTop(elm: HTMLElement | null, value?: number): number | void { export function scrollTop(
elm: HTMLElement | false | null | undefined,
value?: number
): number | void {
return getSetProp('scrollTop', 0, elm, value) as number; return getSetProp('scrollTop', 0, elm, value) as number;
} }
@@ -70,8 +104,11 @@ export function scrollTop(elm: HTMLElement | null, value?: number): number | voi
* @param elm The input element of which the value shall be get or set. * @param elm The input element of which the value shall be get or set.
* @param value The value which shall be set. * @param value The value which shall be set.
*/ */
export function val(elm: HTMLInputElement | null): string; export function val(elm: HTMLInputElement | false | null | undefined): string;
export function val(elm: HTMLInputElement | null, value: string): void; export function val(elm: HTMLInputElement | false | null | undefined, value: string): void;
export function val(elm: HTMLInputElement | null, value?: string): string | void { export function val(
elm: HTMLInputElement | false | null | undefined,
value?: string
): string | void {
return getSetProp('value', '', elm, value) as string; return getSetProp('value', '', elm, value) as string;
} }
@@ -5,14 +5,14 @@ import { keys } from 'support/utils/object';
const rnothtmlwhite = /[^\x20\t\r\n\f]+/g; const rnothtmlwhite = /[^\x20\t\r\n\f]+/g;
const classListAction = ( const classListAction = (
elm: Element | false | null | undefined, elm: Element | false | null | undefined,
className: string, className: string | false | null | undefined,
action: (elmClassList: DOMTokenList, clazz: string) => boolean | void action: (elmClassList: DOMTokenList, clazz: string) => boolean | void
): boolean => { ): boolean => {
let clazz: string; let clazz: string;
let i = 0; let i = 0;
let result = false; let result = false;
if (elm && isString(className)) { if (elm && className && isString(className)) {
const classes: Array<string> = className.match(rnothtmlwhite) || []; const classes: Array<string> = className.match(rnothtmlwhite) || [];
result = classes.length > 0; result = classes.length > 0;
while ((clazz = classes[i++])) { while ((clazz = classes[i++])) {
@@ -27,15 +27,20 @@ const classListAction = (
* @param elm The element. * @param elm The element.
* @param className The class name(s). * @param className The class name(s).
*/ */
export const hasClass = (elm: Element | false | null | undefined, className: string): boolean => export const hasClass = (
classListAction(elm, className, (classList, clazz) => classList.contains(clazz)); elm: Element | false | null | undefined,
className: string | false | null | undefined
): boolean => classListAction(elm, className, (classList, clazz) => classList.contains(clazz));
/** /**
* Removes the given class name(s) from the given element. * Removes the given class name(s) from the given element.
* @param elm The element. * @param elm The element.
* @param className The class name(s) which shall be removed. (separated by spaces) * @param className The class name(s) which shall be removed. (separated by spaces)
*/ */
export const removeClass = (elm: Element | false | null | undefined, className: string): void => { export const removeClass = (
elm: Element | false | null | undefined,
className: string | false | null | undefined
): void => {
classListAction(elm, className, (classList, clazz) => classList.remove(clazz)); classListAction(elm, className, (classList, clazz) => classList.remove(clazz));
}; };
@@ -47,7 +52,7 @@ export const removeClass = (elm: Element | false | null | undefined, className:
*/ */
export const addClass = ( export const addClass = (
elm: Element | false | null | undefined, elm: Element | false | null | undefined,
className: string className: string | false | null | undefined
): (() => void) => { ): (() => void) => {
classListAction(elm, className, (classList, clazz) => classList.add(clazz)); classListAction(elm, className, (classList, clazz) => classList.add(clazz));
return removeClass.bind(0, elm, className); return removeClass.bind(0, elm, className);
@@ -110,7 +110,7 @@ let updateCount = 0;
const osInstance = const osInstance =
// @ts-ignore // @ts-ignore
(window.os = OverlayScrollbars( (window.os = OverlayScrollbars(
{ target: target!, content: useContentElement }, { target: target!, viewport: target!, content: useContentElement },
{ nativeScrollbarsOverlaid: { initialize: true } }, { nativeScrollbarsOverlaid: { initialize: true } },
{ {
updated() { updated() {
@@ -255,18 +255,23 @@ selectCallbackEnv(containerDirectionSelect);
selectCallbackEnv(containerMinMaxSelect); selectCallbackEnv(containerMinMaxSelect);
const checkMetrics = async (checkComparison: CheckComparisonObj) => { const checkMetrics = async (checkComparison: CheckComparisonObj) => {
const {
host: targetHost,
viewport: targetViewport,
padding: targetPadding,
} = osInstance.elements();
const viewportIsTarget = targetHost === targetViewport;
const { metrics: oldMetrics, updCount: oldUpdCount } = checkComparison; const { metrics: oldMetrics, updCount: oldUpdCount } = checkComparison;
const currMetrics = getMetrics(comparison!); const currMetrics = getMetrics(comparison!);
await waitForOrFailTest(async () => { await waitForOrFailTest(async () => {
if (!metricsDimensionsEqual(oldMetrics, currMetrics)) { if (!viewportIsTarget && !metricsDimensionsEqual(oldMetrics, currMetrics)) {
should.ok(updateCount > oldUpdCount, 'Update should have been triggered.'); should.ok(updateCount > oldUpdCount, 'Update should have been triggered.');
} }
}); });
await waitForOrFailTest(async () => { await waitForOrFailTest(async () => {
const comparisonMetrics = getMetrics(comparison!); const comparisonMetrics = getMetrics(comparison!);
const targetMetrics = getMetrics(target!); const targetMetrics = getMetrics(target!);
const targetViewport = osInstance.elements().viewport;
const targetPadding = osInstance.elements().padding;
const { x: overflowOptionX, y: overflowOptionY } = osInstance.options().overflow; const { x: overflowOptionX, y: overflowOptionY } = osInstance.options().overflow;
const overflowOptionXVisible = isVisibleOverflow(overflowOptionX); const overflowOptionXVisible = isVisibleOverflow(overflowOptionX);
const overflowOptionYVisible = isVisibleOverflow(overflowOptionY); const overflowOptionYVisible = isVisibleOverflow(overflowOptionY);
@@ -446,7 +451,7 @@ const checkMetrics = async (checkComparison: CheckComparisonObj) => {
'visible', 'visible',
'Host Overflow should be visible with visible overflowing content.' 'Host Overflow should be visible with visible overflowing content.'
); );
} else { } else if (!viewportIsTarget) {
should.equal( should.equal(
hostOverflowStyle, hostOverflowStyle,
'hidden', 'hidden',
@@ -89,13 +89,14 @@ body {
} }
} }
#target::after { #target[data-overlayscrollbars~='host']::after {
display: none; display: none;
} }
#target[data-overlayscrollbars~='viewport'],
#comparison { #comparison {
overflow: hidden;
z-index: 0; z-index: 0;
overflow: hidden;
} }
.resize { .resize {
@@ -152,7 +153,7 @@ body {
width: auto; width: auto;
display: inline-block; display: inline-block;
} }
.widthAuto > #target { .widthAuto > #target[data-overlayscrollbars~='host'] {
display: inline-flex; display: inline-flex;
} }
.widthHundred > .container, .widthHundred > .container,
+15 -16
View File
@@ -62,23 +62,22 @@ type RemoveEventListener<NameArgsMap extends Record<string, any>> = <Name extend
type InitialEventListeners<NameArgsMap extends Record<string, any>> = { type InitialEventListeners<NameArgsMap extends Record<string, any>> = {
[K in Extract<keyof NameArgsMap, string>]?: EventListenerGroup<NameArgsMap, K>; [K in Extract<keyof NameArgsMap, string>]?: EventListenerGroup<NameArgsMap, K>;
}; };
type ResizeBehavior = "none" | "both" | "horizontal" | "vertical";
type OverflowBehavior = "hidden" | "scroll" | "visible" | "visible-hidden" | "visible-scroll"; type OverflowBehavior = "hidden" | "scroll" | "visible" | "visible-hidden" | "visible-scroll";
type VisibilityBehavior = "visible" | "hidden" | "auto"; type VisibilityBehavior = "visible" | "hidden" | "auto";
type AutoHideBehavior = "never" | "scroll" | "leave" | "move"; type AutoHideBehavior = "never" | "scroll" | "leave" | "move";
interface OSOptions { interface OSOptions {
resize: ResizeBehavior;
paddingAbsolute: boolean; paddingAbsolute: boolean;
updating: { updating: {
elementEvents: Array<[ elementEvents: Array<[
string, elementSelector: string,
string eventNames: string
]> | null; ]> | null;
attributes: string[] | null; attributes: string[] | null;
debounce: number | [ debounce: [
number, timeout: number,
number maxWait: number
] | null; ] | number | null; // (if tuple: [timeout: 0, maxWait: 33], if number: [timeout: number, maxWait: false]) debounce for content Changes
ignoreMutation: ((mutation: MutationRecord) => any) | null;
}; };
overflow: { overflow: {
x: OverflowBehavior; x: OverflowBehavior;
@@ -92,18 +91,10 @@ 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;
};
} }
type StructureInitializationStrategyElementFn<T> = ((target: OSTargetElement) => HTMLElement | T) | T; type StructureInitializationStrategyElementFn<T> = ((target: OSTargetElement) => HTMLElement | T) | T;
type ScrollbarsInitializationStrategyElementFn<T> = ((target: OSTargetElement, host: HTMLElement, viewport: HTMLElement) => HTMLElement | T) | T; type ScrollbarsInitializationStrategyElementFn<T> = ((target: OSTargetElement, host: HTMLElement, viewport: HTMLElement) => HTMLElement | T) | T;
@@ -203,12 +194,20 @@ interface OverlayScrollbarsState {
overflowStyle: XY<OverflowStyle>; overflowStyle: XY<OverflowStyle>;
hasOverflow: XY<boolean>; hasOverflow: XY<boolean>;
} }
interface OverlayScrollbarsElements {
target: HTMLElement;
host: HTMLElement;
padding: HTMLElement;
viewport: HTMLElement;
content: HTMLElement;
}
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(): OverlayScrollbarsState; state(): OverlayScrollbarsState;
elements(): OverlayScrollbarsElements;
on: AddOSEventListener; on: AddOSEventListener;
off: RemoveOSEventListener; off: RemoveOSEventListener;
} }