mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-21 15:30:36 +03:00
overflowChanged event listener
This commit is contained in:
+118
-100
@@ -471,6 +471,7 @@ const equal = (a, b, props, propMutation) => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
const equalWH = (a, b) => equal(a, b, ['w', 'h']);
|
const equalWH = (a, b) => equal(a, b, ['w', 'h']);
|
||||||
|
const equalXY = (a, b) => equal(a, b, ['x', 'y']);
|
||||||
const equalTRBL = (a, b) => equal(a, b, ['t', 'r', 'b', 'l']);
|
const equalTRBL = (a, b) => equal(a, b, ['t', 'r', 'b', 'l']);
|
||||||
const equalBCRWH = (a, b, round) => equal(a, b, ['width', 'height'], round && (value => Math.round(value)));
|
const equalBCRWH = (a, b, round) => equal(a, b, ['width', 'height'], round && (value => Math.round(value)));
|
||||||
|
|
||||||
@@ -1288,18 +1289,9 @@ const createSizeObserver = (target, onSizeChangedCallback, options) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
prependChildren(target, sizeObserver);
|
prependChildren(target, sizeObserver);
|
||||||
return {
|
return () => {
|
||||||
_destroy() {
|
runEach(offListeners);
|
||||||
runEach(offListeners);
|
removeElements(sizeObserver);
|
||||||
removeElements(sizeObserver);
|
|
||||||
},
|
|
||||||
|
|
||||||
_getCurrentCacheValues(force) {
|
|
||||||
return {
|
|
||||||
_directionIsRTL: directionIsRTLCache ? directionIsRTLCache[1](force) : [false, false, false]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1308,7 +1300,7 @@ const isHeightIntrinsic = ioEntryOrSize => ioEntryOrSize.h === 0 || ioEntryOrSiz
|
|||||||
const createTrinsicObserver = (target, onTrinsicChangedCallback) => {
|
const createTrinsicObserver = (target, onTrinsicChangedCallback) => {
|
||||||
const trinsicObserver = createDiv(classNameTrinsicObserver);
|
const trinsicObserver = createDiv(classNameTrinsicObserver);
|
||||||
const offListeners = [];
|
const offListeners = [];
|
||||||
const [updateHeightIntrinsicCache, getCurrentHeightIntrinsicCache] = createCache({
|
const [updateHeightIntrinsicCache] = createCache({
|
||||||
_initialValue: false
|
_initialValue: false
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1341,23 +1333,14 @@ const createTrinsicObserver = (target, onTrinsicChangedCallback) => {
|
|||||||
triggerOnTrinsicChangedCallback(newSize);
|
triggerOnTrinsicChangedCallback(newSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
push(offListeners, createSizeObserver(trinsicObserver, onSizeChanged)._destroy);
|
push(offListeners, createSizeObserver(trinsicObserver, onSizeChanged));
|
||||||
onSizeChanged();
|
onSizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
prependChildren(target, trinsicObserver);
|
prependChildren(target, trinsicObserver);
|
||||||
return {
|
return () => {
|
||||||
_destroy() {
|
runEach(offListeners);
|
||||||
runEach(offListeners);
|
removeElements(trinsicObserver);
|
||||||
removeElements(trinsicObserver);
|
|
||||||
},
|
|
||||||
|
|
||||||
_getCurrentCacheValues(force) {
|
|
||||||
return {
|
|
||||||
_heightIntrinsic: getCurrentHeightIntrinsicCache(force)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1640,8 +1623,8 @@ const lifecycleHubOservers = (instance, updateLifecycles) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const trinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
|
const destroyTrinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
|
||||||
const sizeObserver = createSizeObserver(_host, onSizeChanged, {
|
const destroySizeObserver = createSizeObserver(_host, onSizeChanged, {
|
||||||
_appear: true,
|
_appear: true,
|
||||||
_direction: !_nativeScrollbarStyling
|
_direction: !_nativeScrollbarStyling
|
||||||
});
|
});
|
||||||
@@ -1691,21 +1674,13 @@ const lifecycleHubOservers = (instance, updateLifecycles) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
updateViewportAttrsFromHost();
|
updateViewportAttrsFromHost();
|
||||||
return {
|
return [updateOptions, () => {
|
||||||
_trinsicObserver: trinsicObserver,
|
contentMutationObserver && contentMutationObserver._destroy();
|
||||||
_sizeObserver: sizeObserver,
|
destroyTrinsicObserver && destroyTrinsicObserver();
|
||||||
_updateObserverOptions: updateOptions,
|
destroySizeObserver();
|
||||||
|
|
||||||
_destroy() {
|
hostMutationObserver._destroy();
|
||||||
contentMutationObserver && contentMutationObserver._destroy();
|
}];
|
||||||
trinsicObserver && trinsicObserver._destroy();
|
|
||||||
|
|
||||||
sizeObserver._destroy();
|
|
||||||
|
|
||||||
hostMutationObserver._destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createTrinsicLifecycle = lifecycleHub => {
|
const createTrinsicLifecycle = lifecycleHub => {
|
||||||
@@ -1820,6 +1795,13 @@ const whCacheOptions = {
|
|||||||
h: 0
|
h: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const xyCacheOptions = {
|
||||||
|
_equal: equalXY,
|
||||||
|
_initialValue: {
|
||||||
|
x: false,
|
||||||
|
y: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const sizeFraction = elm => {
|
const sizeFraction = elm => {
|
||||||
const viewportOffsetSize = offsetSize(elm);
|
const viewportOffsetSize = offsetSize(elm);
|
||||||
@@ -1873,6 +1855,7 @@ const createOverflowLifecycle = lifecycleHub => {
|
|||||||
const [updateViewportSizeFraction, getCurrentViewportSizeFraction] = createCache(whCacheOptions, sizeFraction.bind(0, _viewport));
|
const [updateViewportSizeFraction, getCurrentViewportSizeFraction] = createCache(whCacheOptions, sizeFraction.bind(0, _viewport));
|
||||||
const [updateViewportScrollSizeCache, getCurrentViewportScrollSizeCache] = createCache(whCacheOptions, scrollSize.bind(0, _viewport));
|
const [updateViewportScrollSizeCache, getCurrentViewportScrollSizeCache] = createCache(whCacheOptions, scrollSize.bind(0, _viewport));
|
||||||
const [updateOverflowAmountCache, getCurrentOverflowAmountCache] = createCache(whCacheOptions);
|
const [updateOverflowAmountCache, getCurrentOverflowAmountCache] = createCache(whCacheOptions);
|
||||||
|
const [updateOverflowScrollCache] = createCache(xyCacheOptions);
|
||||||
|
|
||||||
const fixFlexboxGlue = (viewportOverflowState, heightIntrinsic) => {
|
const fixFlexboxGlue = (viewportOverflowState, heightIntrinsic) => {
|
||||||
style(_viewport, {
|
style(_viewport, {
|
||||||
@@ -2189,8 +2172,8 @@ const createOverflowLifecycle = lifecycleHub => {
|
|||||||
style(_viewport, viewportStyle);
|
style(_viewport, viewportStyle);
|
||||||
|
|
||||||
_setLifecycleCommunication({
|
_setLifecycleCommunication({
|
||||||
_viewportOverflowScroll: viewportOverflowState._overflowScroll,
|
_viewportOverflowScrollCache: updateOverflowScrollCache(viewportOverflowState._overflowScroll),
|
||||||
_viewportOverflowAmount: overflowAmount
|
_viewportOverflowAmountCache: overflowAmuntCache
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2198,6 +2181,8 @@ const createOverflowLifecycle = lifecycleHub => {
|
|||||||
|
|
||||||
const getPropByPath = (obj, path) => obj ? path.split('.').reduce((o, prop) => o && hasOwnProperty(o, prop) ? o[prop] : undefined, obj) : undefined;
|
const getPropByPath = (obj, path) => obj ? path.split('.').reduce((o, prop) => o && hasOwnProperty(o, prop) ? o[prop] : undefined, obj) : undefined;
|
||||||
|
|
||||||
|
const applyForceToCache = (cacheValues, force) => [cacheValues[0], force || cacheValues[1], cacheValues[2]];
|
||||||
|
|
||||||
const booleanCacheValuesFallback = [false, false, false];
|
const booleanCacheValuesFallback = [false, false, false];
|
||||||
const lifecycleCommunicationFallback = {
|
const lifecycleCommunicationFallback = {
|
||||||
_paddingInfo: {
|
_paddingInfo: {
|
||||||
@@ -2209,14 +2194,14 @@ const lifecycleCommunicationFallback = {
|
|||||||
l: 0
|
l: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_viewportOverflowScroll: {
|
_viewportOverflowScrollCache: [{
|
||||||
x: false,
|
x: false,
|
||||||
y: false
|
y: false
|
||||||
},
|
}, false],
|
||||||
_viewportOverflowAmount: {
|
_viewportOverflowAmountCache: [{
|
||||||
w: 0,
|
w: 0,
|
||||||
h: 0
|
h: 0
|
||||||
},
|
}, false],
|
||||||
_viewportPaddingStyle: {
|
_viewportPaddingStyle: {
|
||||||
marginRight: 0,
|
marginRight: 0,
|
||||||
marginBottom: 0,
|
marginBottom: 0,
|
||||||
@@ -2227,8 +2212,35 @@ const lifecycleCommunicationFallback = {
|
|||||||
paddingLeft: 0
|
paddingLeft: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const createLifecycleHub = (options, triggerEvent, structureSetup, scrollbarsSetup) => {
|
|
||||||
|
const prepareUpdateHints = (leading, adaptive, force) => {
|
||||||
|
const result = {};
|
||||||
|
const finalAdaptive = adaptive || {};
|
||||||
|
const objKeys = keys(leading).concat(keys(finalAdaptive));
|
||||||
|
each(objKeys, key => {
|
||||||
|
const leadingValue = leading[key];
|
||||||
|
const adaptiveValue = finalAdaptive[key];
|
||||||
|
result[key] = isBoolean(leadingValue) ? !!force || !!leadingValue || !!adaptiveValue : applyForceToCache(leadingValue || booleanCacheValuesFallback, force);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createOverflowChangedArgs = (overflowAmount, overflowScroll) => ({
|
||||||
|
amount: {
|
||||||
|
x: overflowAmount.w,
|
||||||
|
y: overflowAmount.h
|
||||||
|
},
|
||||||
|
overflow: {
|
||||||
|
x: overflowAmount.w > 0,
|
||||||
|
y: overflowAmount.h > 0
|
||||||
|
},
|
||||||
|
scrollableOverflow: assignDeep({}, overflowScroll)
|
||||||
|
});
|
||||||
|
|
||||||
|
const createLifecycleHub = (options, triggerListener, structureSetup, scrollbarsSetup) => {
|
||||||
let lifecycleCommunication = lifecycleCommunicationFallback;
|
let lifecycleCommunication = lifecycleCommunicationFallback;
|
||||||
|
let updateObserverOptions;
|
||||||
|
let destroyObservers;
|
||||||
const {
|
const {
|
||||||
_viewport
|
_viewport
|
||||||
} = structureSetup._targetObj;
|
} = structureSetup._targetObj;
|
||||||
@@ -2254,16 +2266,14 @@ const createLifecycleHub = (options, triggerEvent, structureSetup, scrollbarsSet
|
|||||||
const lifecycles = [createTrinsicLifecycle(instance), createPaddingLifecycle(instance), createOverflowLifecycle(instance)];
|
const lifecycles = [createTrinsicLifecycle(instance), createPaddingLifecycle(instance), createOverflowLifecycle(instance)];
|
||||||
|
|
||||||
const updateLifecycles = (updateHints, changedOptions, force) => {
|
const updateLifecycles = (updateHints, changedOptions, force) => {
|
||||||
let {
|
const initialUpdateHints = prepareUpdateHints(assignDeep({
|
||||||
_directionIsRTL,
|
_sizeChanged: false,
|
||||||
_heightIntrinsic,
|
_hostMutation: false,
|
||||||
_sizeChanged = force || false,
|
_contentMutation: false,
|
||||||
_hostMutation = force || false,
|
_paddingStyleChanged: false,
|
||||||
_contentMutation = force || false,
|
_directionIsRTL: booleanCacheValuesFallback,
|
||||||
_paddingStyleChanged = force || false
|
_heightIntrinsic: booleanCacheValuesFallback
|
||||||
} = updateHints || {};
|
}, updateHints), {}, force);
|
||||||
const finalDirectionIsRTL = _directionIsRTL || (_sizeObserver ? _sizeObserver._getCurrentCacheValues(force)._directionIsRTL : booleanCacheValuesFallback);
|
|
||||||
const finalHeightIntrinsic = _heightIntrinsic || (_trinsicObserver ? _trinsicObserver._getCurrentCacheValues(force)._heightIntrinsic : booleanCacheValuesFallback);
|
|
||||||
|
|
||||||
const checkOption = path => [getPropByPath(options, path), force || getPropByPath(changedOptions, path) !== undefined];
|
const checkOption = path => [getPropByPath(options, path), force || getPropByPath(changedOptions, path) !== undefined];
|
||||||
|
|
||||||
@@ -2271,28 +2281,13 @@ const createLifecycleHub = (options, triggerEvent, structureSetup, scrollbarsSet
|
|||||||
const scrollOffsetX = adjustScrollOffset && scrollLeft(_viewport);
|
const scrollOffsetX = adjustScrollOffset && scrollLeft(_viewport);
|
||||||
const scrollOffsetY = adjustScrollOffset && scrollTop(_viewport);
|
const scrollOffsetY = adjustScrollOffset && scrollTop(_viewport);
|
||||||
|
|
||||||
if (_updateObserverOptions) {
|
if (updateObserverOptions) {
|
||||||
_updateObserverOptions(checkOption);
|
updateObserverOptions(checkOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let adaptivedUpdateHints = initialUpdateHints;
|
||||||
each(lifecycles, lifecycle => {
|
each(lifecycles, lifecycle => {
|
||||||
const {
|
adaptivedUpdateHints = prepareUpdateHints(adaptivedUpdateHints, lifecycle(adaptivedUpdateHints, checkOption, !!force) || {}, force);
|
||||||
_sizeChanged: adaptiveSizeChanged,
|
|
||||||
_hostMutation: adaptiveHostMutation,
|
|
||||||
_contentMutation: adaptiveContentMutation,
|
|
||||||
_paddingStyleChanged: adaptivePaddingStyleChanged
|
|
||||||
} = lifecycle({
|
|
||||||
_directionIsRTL: finalDirectionIsRTL,
|
|
||||||
_heightIntrinsic: finalHeightIntrinsic,
|
|
||||||
_sizeChanged,
|
|
||||||
_hostMutation,
|
|
||||||
_contentMutation,
|
|
||||||
_paddingStyleChanged
|
|
||||||
}, checkOption, !!force) || {};
|
|
||||||
_sizeChanged = adaptiveSizeChanged || _sizeChanged;
|
|
||||||
_hostMutation = adaptiveHostMutation || _hostMutation;
|
|
||||||
_contentMutation = adaptiveContentMutation || _contentMutation;
|
|
||||||
_paddingStyleChanged = adaptivePaddingStyleChanged || _paddingStyleChanged;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isNumber(scrollOffsetX)) {
|
if (isNumber(scrollOffsetX)) {
|
||||||
@@ -2303,35 +2298,42 @@ const createLifecycleHub = (options, triggerEvent, structureSetup, scrollbarsSet
|
|||||||
scrollTop(_viewport, scrollOffsetY);
|
scrollTop(_viewport, scrollOffsetY);
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerEvent('updated', {
|
const {
|
||||||
|
_viewportOverflowAmountCache: overflowAmountCache,
|
||||||
|
_viewportOverflowScrollCache: overflowScrollCache
|
||||||
|
} = lifecycleCommunication;
|
||||||
|
const [overflowAmount, overflowAmountChanged, prevOverflowAmount] = overflowAmountCache;
|
||||||
|
const [overflowScroll, overflowScrollChanged, prevOverflowScroll] = overflowScrollCache;
|
||||||
|
|
||||||
|
if (overflowAmountChanged || overflowScrollChanged) {
|
||||||
|
triggerListener('overflowChanged', assignDeep({}, createOverflowChangedArgs(overflowAmount, overflowScroll), {
|
||||||
|
previous: createOverflowChangedArgs(prevOverflowAmount, prevOverflowScroll)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerListener('updated', {
|
||||||
updateHints: {
|
updateHints: {
|
||||||
sizeChanged: _sizeChanged,
|
sizeChanged: adaptivedUpdateHints._sizeChanged,
|
||||||
contentMutation: _contentMutation,
|
contentMutation: adaptivedUpdateHints._contentMutation,
|
||||||
hostMutation: _hostMutation,
|
hostMutation: adaptivedUpdateHints._hostMutation,
|
||||||
directionChanged: finalDirectionIsRTL[1],
|
directionChanged: adaptivedUpdateHints._directionIsRTL[1],
|
||||||
heightIntrinsicChanged: finalHeightIntrinsic[1]
|
heightIntrinsicChanged: adaptivedUpdateHints._heightIntrinsic[1]
|
||||||
},
|
},
|
||||||
changedOptions: changedOptions || {},
|
changedOptions: changedOptions || {},
|
||||||
force: !!force
|
force: !!force
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
[updateObserverOptions, destroyObservers] = lifecycleHubOservers(instance, updateLifecycles);
|
||||||
_sizeObserver,
|
|
||||||
_trinsicObserver,
|
|
||||||
_updateObserverOptions,
|
|
||||||
_destroy: destroyObservers
|
|
||||||
} = lifecycleHubOservers(instance, updateLifecycles);
|
|
||||||
|
|
||||||
const update = (changedOptions, force) => updateLifecycles({}, changedOptions, force);
|
const update = (changedOptions, force) => updateLifecycles({}, changedOptions, force);
|
||||||
|
|
||||||
const envUpdateListener = update.bind(0, {}, true);
|
const envUpdateListener = update.bind(0, {}, true);
|
||||||
addEnvironmentListener(envUpdateListener);
|
addEnvironmentListener(envUpdateListener);
|
||||||
console.log(getEnvironment());
|
|
||||||
return {
|
return {
|
||||||
_update: update,
|
_update: update,
|
||||||
_state: () => ({
|
_state: () => ({
|
||||||
_overflowAmount: lifecycleCommunication._viewportOverflowAmount
|
_overflowAmount: lifecycleCommunication._viewportOverflowAmountCache[0]
|
||||||
}),
|
}),
|
||||||
|
|
||||||
_destroy() {
|
_destroy() {
|
||||||
@@ -2417,7 +2419,7 @@ const manageListener = (callback, listener) => {
|
|||||||
each(isArray(listener) ? listener : [listener], callback);
|
each(isArray(listener) ? listener : [listener], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createEventHub = () => {
|
const createEventListenerHub = initialEventListeners => {
|
||||||
const events = new Map();
|
const events = new Map();
|
||||||
|
|
||||||
const removeEvent = (name, listener) => {
|
const removeEvent = (name, listener) => {
|
||||||
@@ -2448,14 +2450,27 @@ const createEventHub = () => {
|
|||||||
const triggerEvent = (name, args) => {
|
const triggerEvent = (name, args) => {
|
||||||
const eventSet = events.get(name);
|
const eventSet = events.get(name);
|
||||||
each(from(eventSet), event => {
|
each(from(eventSet), event => {
|
||||||
event(args);
|
if (args) {
|
||||||
|
event(args);
|
||||||
|
} else {
|
||||||
|
event();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const initialListenerKeys = keys(initialEventListeners);
|
||||||
|
each(initialListenerKeys, key => {
|
||||||
|
addEvent(key, initialEventListeners[key]);
|
||||||
|
});
|
||||||
return [addEvent, removeEvent, triggerEvent];
|
return [addEvent, removeEvent, triggerEvent];
|
||||||
};
|
};
|
||||||
|
|
||||||
const OverlayScrollbars = (target, options) => {
|
const OverlayScrollbars = (target, options, eventListeners) => {
|
||||||
|
const {
|
||||||
|
_getDefaultOptions,
|
||||||
|
_nativeScrollbarIsOverlaid
|
||||||
|
} = getEnvironment();
|
||||||
|
const plugins = getPlugins();
|
||||||
const instanceTarget = isHTMLElement(target) ? target : target.target;
|
const instanceTarget = isHTMLElement(target) ? target : target.target;
|
||||||
const potentialInstance = getInstance(instanceTarget);
|
const potentialInstance = getInstance(instanceTarget);
|
||||||
|
|
||||||
@@ -2463,10 +2478,6 @@ const OverlayScrollbars = (target, options) => {
|
|||||||
return potentialInstance;
|
return potentialInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
|
||||||
_getDefaultOptions
|
|
||||||
} = getEnvironment();
|
|
||||||
const plugins = getPlugins();
|
|
||||||
const optionsValidationPlugin = plugins[optionsValidationPluginName];
|
const optionsValidationPlugin = plugins[optionsValidationPluginName];
|
||||||
|
|
||||||
const validateOptions = newOptions => {
|
const validateOptions = newOptions => {
|
||||||
@@ -2475,8 +2486,13 @@ const OverlayScrollbars = (target, options) => {
|
|||||||
return validate ? validate(opts, true) : opts;
|
return validate ? validate(opts, true) : opts;
|
||||||
};
|
};
|
||||||
|
|
||||||
const [addEvent, removeEvent, triggerEvent] = createEventHub();
|
|
||||||
const currentOptions = assignDeep({}, _getDefaultOptions(), validateOptions(options));
|
const currentOptions = assignDeep({}, _getDefaultOptions(), validateOptions(options));
|
||||||
|
const [addEvent, removeEvent, triggerEvent] = createEventListenerHub(eventListeners);
|
||||||
|
|
||||||
|
if (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y && !currentOptions.nativeScrollbarsOverlaid.initialize) {
|
||||||
|
triggerEvent('initializationWithdrawn', false);
|
||||||
|
}
|
||||||
|
|
||||||
const structureSetup = createStructureSetup(target);
|
const structureSetup = createStructureSetup(target);
|
||||||
const scrollbarsSetup = createScrollbarsSetup(target, structureSetup);
|
const scrollbarsSetup = createScrollbarsSetup(target, structureSetup);
|
||||||
const lifecycleHub = createLifecycleHub(currentOptions, triggerEvent, structureSetup, scrollbarsSetup);
|
const lifecycleHub = createLifecycleHub(currentOptions, triggerEvent, structureSetup, scrollbarsSetup);
|
||||||
@@ -2508,6 +2524,7 @@ const OverlayScrollbars = (target, options) => {
|
|||||||
|
|
||||||
removeInstance(instanceTarget);
|
removeInstance(instanceTarget);
|
||||||
removeEvent();
|
removeEvent();
|
||||||
|
triggerEvent('destroyed', false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
each(keys(plugins), pluginName => {
|
each(keys(plugins), pluginName => {
|
||||||
@@ -2519,6 +2536,7 @@ const OverlayScrollbars = (target, options) => {
|
|||||||
});
|
});
|
||||||
instance.update(true);
|
instance.update(true);
|
||||||
addInstance(instanceTarget, instance);
|
addInstance(instanceTarget, instance);
|
||||||
|
triggerEvent('initialized', false);
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
OverlayScrollbars.extend = addPlugin;
|
OverlayScrollbars.extend = addPlugin;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+136
-102
@@ -532,6 +532,9 @@
|
|||||||
var equalWH = function equalWH(a, b) {
|
var equalWH = function equalWH(a, b) {
|
||||||
return equal(a, b, ['w', 'h']);
|
return equal(a, b, ['w', 'h']);
|
||||||
};
|
};
|
||||||
|
var equalXY = function equalXY(a, b) {
|
||||||
|
return equal(a, b, ['x', 'y']);
|
||||||
|
};
|
||||||
var equalTRBL = function equalTRBL(a, b) {
|
var equalTRBL = function equalTRBL(a, b) {
|
||||||
return equal(a, b, ['t', 'r', 'b', 'l']);
|
return equal(a, b, ['t', 'r', 'b', 'l']);
|
||||||
};
|
};
|
||||||
@@ -1363,16 +1366,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
prependChildren(target, sizeObserver);
|
prependChildren(target, sizeObserver);
|
||||||
return {
|
return function () {
|
||||||
_destroy: function _destroy() {
|
runEach(offListeners);
|
||||||
runEach(offListeners);
|
removeElements(sizeObserver);
|
||||||
removeElements(sizeObserver);
|
|
||||||
},
|
|
||||||
_getCurrentCacheValues: function _getCurrentCacheValues(force) {
|
|
||||||
return {
|
|
||||||
_directionIsRTL: directionIsRTLCache ? directionIsRTLCache[1](force) : [false, false, false]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1387,8 +1383,7 @@
|
|||||||
var _createCache = createCache({
|
var _createCache = createCache({
|
||||||
_initialValue: false
|
_initialValue: false
|
||||||
}),
|
}),
|
||||||
updateHeightIntrinsicCache = _createCache[0],
|
updateHeightIntrinsicCache = _createCache[0];
|
||||||
getCurrentHeightIntrinsicCache = _createCache[1];
|
|
||||||
|
|
||||||
var triggerOnTrinsicChangedCallback = function triggerOnTrinsicChangedCallback(updateValue) {
|
var triggerOnTrinsicChangedCallback = function triggerOnTrinsicChangedCallback(updateValue) {
|
||||||
if (updateValue) {
|
if (updateValue) {
|
||||||
@@ -1419,21 +1414,14 @@
|
|||||||
triggerOnTrinsicChangedCallback(newSize);
|
triggerOnTrinsicChangedCallback(newSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
push(offListeners, createSizeObserver(trinsicObserver, onSizeChanged)._destroy);
|
push(offListeners, createSizeObserver(trinsicObserver, onSizeChanged));
|
||||||
onSizeChanged();
|
onSizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
prependChildren(target, trinsicObserver);
|
prependChildren(target, trinsicObserver);
|
||||||
return {
|
return function () {
|
||||||
_destroy: function _destroy() {
|
runEach(offListeners);
|
||||||
runEach(offListeners);
|
removeElements(trinsicObserver);
|
||||||
removeElements(trinsicObserver);
|
|
||||||
},
|
|
||||||
_getCurrentCacheValues: function _getCurrentCacheValues(force) {
|
|
||||||
return {
|
|
||||||
_heightIntrinsic: getCurrentHeightIntrinsicCache(force)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1713,8 +1701,8 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var trinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
|
var destroyTrinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
|
||||||
var sizeObserver = createSizeObserver(_host, onSizeChanged, {
|
var destroySizeObserver = createSizeObserver(_host, onSizeChanged, {
|
||||||
_appear: true,
|
_appear: true,
|
||||||
_direction: !_nativeScrollbarStyling
|
_direction: !_nativeScrollbarStyling
|
||||||
});
|
});
|
||||||
@@ -1773,19 +1761,13 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
updateViewportAttrsFromHost();
|
updateViewportAttrsFromHost();
|
||||||
return {
|
return [updateOptions, function () {
|
||||||
_trinsicObserver: trinsicObserver,
|
contentMutationObserver && contentMutationObserver._destroy();
|
||||||
_sizeObserver: sizeObserver,
|
destroyTrinsicObserver && destroyTrinsicObserver();
|
||||||
_updateObserverOptions: updateOptions,
|
destroySizeObserver();
|
||||||
_destroy: function _destroy() {
|
|
||||||
contentMutationObserver && contentMutationObserver._destroy();
|
|
||||||
trinsicObserver && trinsicObserver._destroy();
|
|
||||||
|
|
||||||
sizeObserver._destroy();
|
hostMutationObserver._destroy();
|
||||||
|
}];
|
||||||
hostMutationObserver._destroy();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var createTrinsicLifecycle = function createTrinsicLifecycle(lifecycleHub) {
|
var createTrinsicLifecycle = function createTrinsicLifecycle(lifecycleHub) {
|
||||||
@@ -1903,6 +1885,13 @@
|
|||||||
h: 0
|
h: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
var xyCacheOptions = {
|
||||||
|
_equal: equalXY,
|
||||||
|
_initialValue: {
|
||||||
|
x: false,
|
||||||
|
y: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var sizeFraction = function sizeFraction(elm) {
|
var sizeFraction = function sizeFraction(elm) {
|
||||||
var viewportOffsetSize = offsetSize(elm);
|
var viewportOffsetSize = offsetSize(elm);
|
||||||
@@ -1967,6 +1956,9 @@
|
|||||||
updateOverflowAmountCache = _createCache3[0],
|
updateOverflowAmountCache = _createCache3[0],
|
||||||
getCurrentOverflowAmountCache = _createCache3[1];
|
getCurrentOverflowAmountCache = _createCache3[1];
|
||||||
|
|
||||||
|
var _createCache4 = createCache(xyCacheOptions),
|
||||||
|
updateOverflowScrollCache = _createCache4[0];
|
||||||
|
|
||||||
var fixFlexboxGlue = function fixFlexboxGlue(viewportOverflowState, heightIntrinsic) {
|
var fixFlexboxGlue = function fixFlexboxGlue(viewportOverflowState, heightIntrinsic) {
|
||||||
style(_viewport, {
|
style(_viewport, {
|
||||||
height: ''
|
height: ''
|
||||||
@@ -2277,8 +2269,8 @@
|
|||||||
style(_viewport, viewportStyle);
|
style(_viewport, viewportStyle);
|
||||||
|
|
||||||
_setLifecycleCommunication({
|
_setLifecycleCommunication({
|
||||||
_viewportOverflowScroll: viewportOverflowState._overflowScroll,
|
_viewportOverflowScrollCache: updateOverflowScrollCache(viewportOverflowState._overflowScroll),
|
||||||
_viewportOverflowAmount: overflowAmount
|
_viewportOverflowAmountCache: overflowAmuntCache
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2290,6 +2282,10 @@
|
|||||||
}, obj) : undefined;
|
}, obj) : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var applyForceToCache = function applyForceToCache(cacheValues, force) {
|
||||||
|
return [cacheValues[0], force || cacheValues[1], cacheValues[2]];
|
||||||
|
};
|
||||||
|
|
||||||
var booleanCacheValuesFallback = [false, false, false];
|
var booleanCacheValuesFallback = [false, false, false];
|
||||||
var lifecycleCommunicationFallback = {
|
var lifecycleCommunicationFallback = {
|
||||||
_paddingInfo: {
|
_paddingInfo: {
|
||||||
@@ -2301,14 +2297,14 @@
|
|||||||
l: 0
|
l: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_viewportOverflowScroll: {
|
_viewportOverflowScrollCache: [{
|
||||||
x: false,
|
x: false,
|
||||||
y: false
|
y: false
|
||||||
},
|
}, false],
|
||||||
_viewportOverflowAmount: {
|
_viewportOverflowAmountCache: [{
|
||||||
w: 0,
|
w: 0,
|
||||||
h: 0
|
h: 0
|
||||||
},
|
}, false],
|
||||||
_viewportPaddingStyle: {
|
_viewportPaddingStyle: {
|
||||||
marginRight: 0,
|
marginRight: 0,
|
||||||
marginBottom: 0,
|
marginBottom: 0,
|
||||||
@@ -2319,8 +2315,37 @@
|
|||||||
paddingLeft: 0
|
paddingLeft: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var createLifecycleHub = function createLifecycleHub(options, triggerEvent, structureSetup, scrollbarsSetup) {
|
|
||||||
|
var prepareUpdateHints = function prepareUpdateHints(leading, adaptive, force) {
|
||||||
|
var result = {};
|
||||||
|
var finalAdaptive = adaptive || {};
|
||||||
|
var objKeys = keys(leading).concat(keys(finalAdaptive));
|
||||||
|
each(objKeys, function (key) {
|
||||||
|
var leadingValue = leading[key];
|
||||||
|
var adaptiveValue = finalAdaptive[key];
|
||||||
|
result[key] = isBoolean(leadingValue) ? !!force || !!leadingValue || !!adaptiveValue : applyForceToCache(leadingValue || booleanCacheValuesFallback, force);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
var createOverflowChangedArgs = function createOverflowChangedArgs(overflowAmount, overflowScroll) {
|
||||||
|
return {
|
||||||
|
amount: {
|
||||||
|
x: overflowAmount.w,
|
||||||
|
y: overflowAmount.h
|
||||||
|
},
|
||||||
|
overflow: {
|
||||||
|
x: overflowAmount.w > 0,
|
||||||
|
y: overflowAmount.h > 0
|
||||||
|
},
|
||||||
|
scrollableOverflow: assignDeep({}, overflowScroll)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var createLifecycleHub = function createLifecycleHub(options, triggerListener, structureSetup, scrollbarsSetup) {
|
||||||
var lifecycleCommunication = lifecycleCommunicationFallback;
|
var lifecycleCommunication = lifecycleCommunicationFallback;
|
||||||
|
var updateObserverOptions;
|
||||||
|
var destroyObservers;
|
||||||
var _viewport = structureSetup._targetObj._viewport;
|
var _viewport = structureSetup._targetObj._viewport;
|
||||||
|
|
||||||
var _getEnvironment = getEnvironment(),
|
var _getEnvironment = getEnvironment(),
|
||||||
@@ -2345,20 +2370,14 @@
|
|||||||
var lifecycles = [createTrinsicLifecycle(instance), createPaddingLifecycle(instance), createOverflowLifecycle(instance)];
|
var lifecycles = [createTrinsicLifecycle(instance), createPaddingLifecycle(instance), createOverflowLifecycle(instance)];
|
||||||
|
|
||||||
var updateLifecycles = function updateLifecycles(updateHints, changedOptions, force) {
|
var updateLifecycles = function updateLifecycles(updateHints, changedOptions, force) {
|
||||||
var _ref = updateHints || {},
|
var initialUpdateHints = prepareUpdateHints(assignDeep({
|
||||||
_directionIsRTL = _ref._directionIsRTL,
|
_sizeChanged: false,
|
||||||
_heightIntrinsic = _ref._heightIntrinsic,
|
_hostMutation: false,
|
||||||
_ref$_sizeChanged = _ref._sizeChanged,
|
_contentMutation: false,
|
||||||
_sizeChanged = _ref$_sizeChanged === void 0 ? force || false : _ref$_sizeChanged,
|
_paddingStyleChanged: false,
|
||||||
_ref$_hostMutation = _ref._hostMutation,
|
_directionIsRTL: booleanCacheValuesFallback,
|
||||||
_hostMutation = _ref$_hostMutation === void 0 ? force || false : _ref$_hostMutation,
|
_heightIntrinsic: booleanCacheValuesFallback
|
||||||
_ref$_contentMutation = _ref._contentMutation,
|
}, updateHints), {}, force);
|
||||||
_contentMutation = _ref$_contentMutation === void 0 ? force || false : _ref$_contentMutation,
|
|
||||||
_ref$_paddingStyleCha = _ref._paddingStyleChanged,
|
|
||||||
_paddingStyleChanged = _ref$_paddingStyleCha === void 0 ? force || false : _ref$_paddingStyleCha;
|
|
||||||
|
|
||||||
var finalDirectionIsRTL = _directionIsRTL || (_sizeObserver ? _sizeObserver._getCurrentCacheValues(force)._directionIsRTL : booleanCacheValuesFallback);
|
|
||||||
var finalHeightIntrinsic = _heightIntrinsic || (_trinsicObserver ? _trinsicObserver._getCurrentCacheValues(force)._heightIntrinsic : booleanCacheValuesFallback);
|
|
||||||
|
|
||||||
var checkOption = function checkOption(path) {
|
var checkOption = function checkOption(path) {
|
||||||
return [getPropByPath(options, path), force || getPropByPath(changedOptions, path) !== undefined];
|
return [getPropByPath(options, path), force || getPropByPath(changedOptions, path) !== undefined];
|
||||||
@@ -2368,28 +2387,13 @@
|
|||||||
var scrollOffsetX = adjustScrollOffset && scrollLeft(_viewport);
|
var scrollOffsetX = adjustScrollOffset && scrollLeft(_viewport);
|
||||||
var scrollOffsetY = adjustScrollOffset && scrollTop(_viewport);
|
var scrollOffsetY = adjustScrollOffset && scrollTop(_viewport);
|
||||||
|
|
||||||
if (_updateObserverOptions) {
|
if (updateObserverOptions) {
|
||||||
_updateObserverOptions(checkOption);
|
updateObserverOptions(checkOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var adaptivedUpdateHints = initialUpdateHints;
|
||||||
each(lifecycles, function (lifecycle) {
|
each(lifecycles, function (lifecycle) {
|
||||||
var _ref2 = lifecycle({
|
adaptivedUpdateHints = prepareUpdateHints(adaptivedUpdateHints, lifecycle(adaptivedUpdateHints, checkOption, !!force) || {}, force);
|
||||||
_directionIsRTL: finalDirectionIsRTL,
|
|
||||||
_heightIntrinsic: finalHeightIntrinsic,
|
|
||||||
_sizeChanged: _sizeChanged,
|
|
||||||
_hostMutation: _hostMutation,
|
|
||||||
_contentMutation: _contentMutation,
|
|
||||||
_paddingStyleChanged: _paddingStyleChanged
|
|
||||||
}, checkOption, !!force) || {},
|
|
||||||
adaptiveSizeChanged = _ref2._sizeChanged,
|
|
||||||
adaptiveHostMutation = _ref2._hostMutation,
|
|
||||||
adaptiveContentMutation = _ref2._contentMutation,
|
|
||||||
adaptivePaddingStyleChanged = _ref2._paddingStyleChanged;
|
|
||||||
|
|
||||||
_sizeChanged = adaptiveSizeChanged || _sizeChanged;
|
|
||||||
_hostMutation = adaptiveHostMutation || _hostMutation;
|
|
||||||
_contentMutation = adaptiveContentMutation || _contentMutation;
|
|
||||||
_paddingStyleChanged = adaptivePaddingStyleChanged || _paddingStyleChanged;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isNumber(scrollOffsetX)) {
|
if (isNumber(scrollOffsetX)) {
|
||||||
@@ -2400,24 +2404,39 @@
|
|||||||
scrollTop(_viewport, scrollOffsetY);
|
scrollTop(_viewport, scrollOffsetY);
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerEvent('updated', {
|
var _lifecycleCommunicati = lifecycleCommunication,
|
||||||
|
overflowAmountCache = _lifecycleCommunicati._viewportOverflowAmountCache,
|
||||||
|
overflowScrollCache = _lifecycleCommunicati._viewportOverflowScrollCache;
|
||||||
|
var overflowAmount = overflowAmountCache[0],
|
||||||
|
overflowAmountChanged = overflowAmountCache[1],
|
||||||
|
prevOverflowAmount = overflowAmountCache[2];
|
||||||
|
var overflowScroll = overflowScrollCache[0],
|
||||||
|
overflowScrollChanged = overflowScrollCache[1],
|
||||||
|
prevOverflowScroll = overflowScrollCache[2];
|
||||||
|
|
||||||
|
if (overflowAmountChanged || overflowScrollChanged) {
|
||||||
|
triggerListener('overflowChanged', assignDeep({}, createOverflowChangedArgs(overflowAmount, overflowScroll), {
|
||||||
|
previous: createOverflowChangedArgs(prevOverflowAmount, prevOverflowScroll)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerListener('updated', {
|
||||||
updateHints: {
|
updateHints: {
|
||||||
sizeChanged: _sizeChanged,
|
sizeChanged: adaptivedUpdateHints._sizeChanged,
|
||||||
contentMutation: _contentMutation,
|
contentMutation: adaptivedUpdateHints._contentMutation,
|
||||||
hostMutation: _hostMutation,
|
hostMutation: adaptivedUpdateHints._hostMutation,
|
||||||
directionChanged: finalDirectionIsRTL[1],
|
directionChanged: adaptivedUpdateHints._directionIsRTL[1],
|
||||||
heightIntrinsicChanged: finalHeightIntrinsic[1]
|
heightIntrinsicChanged: adaptivedUpdateHints._heightIntrinsic[1]
|
||||||
},
|
},
|
||||||
changedOptions: changedOptions || {},
|
changedOptions: changedOptions || {},
|
||||||
force: !!force
|
force: !!force
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var _lifecycleHubOservers = lifecycleHubOservers(instance, updateLifecycles),
|
var _lifecycleHubOservers = lifecycleHubOservers(instance, updateLifecycles);
|
||||||
_sizeObserver = _lifecycleHubOservers._sizeObserver,
|
|
||||||
_trinsicObserver = _lifecycleHubOservers._trinsicObserver,
|
updateObserverOptions = _lifecycleHubOservers[0];
|
||||||
_updateObserverOptions = _lifecycleHubOservers._updateObserverOptions,
|
destroyObservers = _lifecycleHubOservers[1];
|
||||||
destroyObservers = _lifecycleHubOservers._destroy;
|
|
||||||
|
|
||||||
var update = function update(changedOptions, force) {
|
var update = function update(changedOptions, force) {
|
||||||
return updateLifecycles({}, changedOptions, force);
|
return updateLifecycles({}, changedOptions, force);
|
||||||
@@ -2425,12 +2444,11 @@
|
|||||||
|
|
||||||
var envUpdateListener = update.bind(0, {}, true);
|
var envUpdateListener = update.bind(0, {}, true);
|
||||||
addEnvironmentListener(envUpdateListener);
|
addEnvironmentListener(envUpdateListener);
|
||||||
console.log(getEnvironment());
|
|
||||||
return {
|
return {
|
||||||
_update: update,
|
_update: update,
|
||||||
_state: function _state() {
|
_state: function _state() {
|
||||||
return {
|
return {
|
||||||
_overflowAmount: lifecycleCommunication._viewportOverflowAmount
|
_overflowAmount: lifecycleCommunication._viewportOverflowAmountCache[0]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
_destroy: function _destroy() {
|
_destroy: function _destroy() {
|
||||||
@@ -2521,7 +2539,7 @@
|
|||||||
each(isArray(listener) ? listener : [listener], callback);
|
each(isArray(listener) ? listener : [listener], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
var createEventHub = function createEventHub() {
|
var createEventListenerHub = function createEventListenerHub(initialEventListeners) {
|
||||||
var events = new Map();
|
var events = new Map();
|
||||||
|
|
||||||
var removeEvent = function removeEvent(name, listener) {
|
var removeEvent = function removeEvent(name, listener) {
|
||||||
@@ -2552,14 +2570,27 @@
|
|||||||
var triggerEvent = function triggerEvent(name, args) {
|
var triggerEvent = function triggerEvent(name, args) {
|
||||||
var eventSet = events.get(name);
|
var eventSet = events.get(name);
|
||||||
each(from(eventSet), function (event) {
|
each(from(eventSet), function (event) {
|
||||||
event(args);
|
if (args) {
|
||||||
|
event(args);
|
||||||
|
} else {
|
||||||
|
event();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var initialListenerKeys = keys(initialEventListeners);
|
||||||
|
each(initialListenerKeys, function (key) {
|
||||||
|
addEvent(key, initialEventListeners[key]);
|
||||||
|
});
|
||||||
return [addEvent, removeEvent, triggerEvent];
|
return [addEvent, removeEvent, triggerEvent];
|
||||||
};
|
};
|
||||||
|
|
||||||
var OverlayScrollbars = function OverlayScrollbars(target, options) {
|
var OverlayScrollbars = function OverlayScrollbars(target, options, eventListeners) {
|
||||||
|
var _getEnvironment = getEnvironment(),
|
||||||
|
_getDefaultOptions = _getEnvironment._getDefaultOptions,
|
||||||
|
_nativeScrollbarIsOverlaid = _getEnvironment._nativeScrollbarIsOverlaid;
|
||||||
|
|
||||||
|
var plugins = getPlugins();
|
||||||
var instanceTarget = isHTMLElement(target) ? target : target.target;
|
var instanceTarget = isHTMLElement(target) ? target : target.target;
|
||||||
var potentialInstance = getInstance(instanceTarget);
|
var potentialInstance = getInstance(instanceTarget);
|
||||||
|
|
||||||
@@ -2567,10 +2598,6 @@
|
|||||||
return potentialInstance;
|
return potentialInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
var _getEnvironment = getEnvironment(),
|
|
||||||
_getDefaultOptions = _getEnvironment._getDefaultOptions;
|
|
||||||
|
|
||||||
var plugins = getPlugins();
|
|
||||||
var optionsValidationPlugin = plugins[optionsValidationPluginName];
|
var optionsValidationPlugin = plugins[optionsValidationPluginName];
|
||||||
|
|
||||||
var validateOptions = function validateOptions(newOptions) {
|
var validateOptions = function validateOptions(newOptions) {
|
||||||
@@ -2579,12 +2606,17 @@
|
|||||||
return validate ? validate(opts, true) : opts;
|
return validate ? validate(opts, true) : opts;
|
||||||
};
|
};
|
||||||
|
|
||||||
var _createEventHub = createEventHub(),
|
|
||||||
addEvent = _createEventHub[0],
|
|
||||||
removeEvent = _createEventHub[1],
|
|
||||||
triggerEvent = _createEventHub[2];
|
|
||||||
|
|
||||||
var currentOptions = assignDeep({}, _getDefaultOptions(), validateOptions(options));
|
var currentOptions = assignDeep({}, _getDefaultOptions(), validateOptions(options));
|
||||||
|
|
||||||
|
var _createEventListenerH = createEventListenerHub(eventListeners),
|
||||||
|
addEvent = _createEventListenerH[0],
|
||||||
|
removeEvent = _createEventListenerH[1],
|
||||||
|
triggerEvent = _createEventListenerH[2];
|
||||||
|
|
||||||
|
if (_nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y && !currentOptions.nativeScrollbarsOverlaid.initialize) {
|
||||||
|
triggerEvent('initializationWithdrawn', false);
|
||||||
|
}
|
||||||
|
|
||||||
var structureSetup = createStructureSetup(target);
|
var structureSetup = createStructureSetup(target);
|
||||||
var scrollbarsSetup = createScrollbarsSetup(target, structureSetup);
|
var scrollbarsSetup = createScrollbarsSetup(target, structureSetup);
|
||||||
var lifecycleHub = createLifecycleHub(currentOptions, triggerEvent, structureSetup, scrollbarsSetup);
|
var lifecycleHub = createLifecycleHub(currentOptions, triggerEvent, structureSetup, scrollbarsSetup);
|
||||||
@@ -2615,6 +2647,7 @@
|
|||||||
|
|
||||||
removeInstance(instanceTarget);
|
removeInstance(instanceTarget);
|
||||||
removeEvent();
|
removeEvent();
|
||||||
|
triggerEvent('destroyed', false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
each(keys(plugins), function (pluginName) {
|
each(keys(plugins), function (pluginName) {
|
||||||
@@ -2626,6 +2659,7 @@
|
|||||||
});
|
});
|
||||||
instance.update(true);
|
instance.update(true);
|
||||||
addInstance(instanceTarget, instance);
|
addInstance(instanceTarget, instance);
|
||||||
|
triggerEvent('initialized', false);
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
OverlayScrollbars.extend = addPlugin;
|
OverlayScrollbars.extend = addPlugin;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,15 +1,15 @@
|
|||||||
import { OSOptions } from 'options';
|
import { OSOptions } from 'options';
|
||||||
import { each, from, isArray, keys } from 'support';
|
import { each, from, isArray, keys, XY } from 'support';
|
||||||
import { PartialOptions } from 'typings';
|
import { PartialOptions } from 'typings';
|
||||||
/*
|
/*
|
||||||
onScrollStart : null,
|
onScrollStart : null,
|
||||||
onScroll : null,
|
onScroll : null,
|
||||||
onScrollStop : null,
|
onScrollStop : null,
|
||||||
onOverflowChanged : null,
|
onOverflowChanged : null,
|
||||||
onOverflowAmountChanged : null,
|
onOverflowAmountChanged : null, // fusion with onOverflowChanged
|
||||||
onDirectionChanged : null,
|
onDirectionChanged : null, // gone
|
||||||
onContentSizeChanged : null,
|
onContentSizeChanged : null, // gone
|
||||||
onHostSizeChanged : null,
|
onHostSizeChanged : null, // gone
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface OnUpdatedEventListenerArgs {
|
export interface OnUpdatedEventListenerArgs {
|
||||||
@@ -24,11 +24,23 @@ export interface OnUpdatedEventListenerArgs {
|
|||||||
force: boolean;
|
force: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OnOverflowChangedEventListenerArgs {
|
||||||
|
overflow: XY<boolean>; // whether there is an overflow
|
||||||
|
scrollableOverflow: XY<boolean>; // whether there is an scrollable overflow
|
||||||
|
amount: XY<number>; // the overflow amount in pixel
|
||||||
|
previous: {
|
||||||
|
overflow: XY<boolean>;
|
||||||
|
scrollableOverflow: XY<boolean>;
|
||||||
|
amount: XY<number>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface EventListenerArgsMap {
|
export interface EventListenerArgsMap {
|
||||||
initialized: false;
|
initialized: false;
|
||||||
initializationWithdrawn: false;
|
initializationWithdrawn: false;
|
||||||
destroyed: false;
|
overflowChanged: OnOverflowChangedEventListenerArgs;
|
||||||
updated: OnUpdatedEventListenerArgs;
|
updated: OnUpdatedEventListenerArgs;
|
||||||
|
destroyed: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OSEventListener<N extends keyof EventListenerArgsMap> = (
|
export type OSEventListener<N extends keyof EventListenerArgsMap> = (
|
||||||
@@ -93,11 +105,15 @@ export const createEventListenerHub = (
|
|||||||
|
|
||||||
return removeEvent.bind(0, name, listener as any);
|
return removeEvent.bind(0, name, listener as any);
|
||||||
};
|
};
|
||||||
const triggerEvent: TriggerEventListener = (name, args) => {
|
const triggerEvent: TriggerEventListener = (name, args?) => {
|
||||||
const eventSet = events.get(name);
|
const eventSet = events.get(name);
|
||||||
|
|
||||||
each(from(eventSet), (event) => {
|
each(from(eventSet), (event) => {
|
||||||
args ? event(args) : (event as () => void)();
|
if (args) {
|
||||||
|
event(args);
|
||||||
|
} else {
|
||||||
|
(event as () => void)();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ export interface LifecycleCommunication {
|
|||||||
_padding: TRBL;
|
_padding: TRBL;
|
||||||
};
|
};
|
||||||
_viewportPaddingStyle: StyleObject;
|
_viewportPaddingStyle: StyleObject;
|
||||||
_viewportOverflowScroll: XY<boolean>;
|
_viewportOverflowScrollCache: CacheValues<XY<boolean>>;
|
||||||
_viewportOverflowAmount: WH<number>;
|
_viewportOverflowAmountCache: CacheValues<WH<number>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LifecycleUpdateHints {
|
export interface LifecycleUpdateHints {
|
||||||
@@ -92,14 +92,20 @@ const lifecycleCommunicationFallback: LifecycleCommunication = {
|
|||||||
l: 0,
|
l: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
_viewportOverflowScroll: {
|
_viewportOverflowScrollCache: [
|
||||||
x: false,
|
{
|
||||||
y: false,
|
x: false,
|
||||||
},
|
y: false,
|
||||||
_viewportOverflowAmount: {
|
},
|
||||||
w: 0,
|
false,
|
||||||
h: 0,
|
],
|
||||||
},
|
_viewportOverflowAmountCache: [
|
||||||
|
{
|
||||||
|
w: 0,
|
||||||
|
h: 0,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
],
|
||||||
_viewportPaddingStyle: {
|
_viewportPaddingStyle: {
|
||||||
marginRight: 0,
|
marginRight: 0,
|
||||||
marginBottom: 0,
|
marginBottom: 0,
|
||||||
@@ -131,6 +137,18 @@ const prepareUpdateHints = <T extends LifecycleUpdateHints>(
|
|||||||
return result as Required<T>;
|
return result as Required<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createOverflowChangedArgs = (overflowAmount: WH<number>, overflowScroll: XY<boolean>) => ({
|
||||||
|
amount: {
|
||||||
|
x: overflowAmount.w,
|
||||||
|
y: overflowAmount.h,
|
||||||
|
},
|
||||||
|
overflow: {
|
||||||
|
x: overflowAmount.w > 0,
|
||||||
|
y: overflowAmount.h > 0,
|
||||||
|
},
|
||||||
|
scrollableOverflow: assignDeep({}, overflowScroll),
|
||||||
|
});
|
||||||
|
|
||||||
export const createLifecycleHub = (
|
export const createLifecycleHub = (
|
||||||
options: OSOptions,
|
options: OSOptions,
|
||||||
triggerListener: TriggerEventListener,
|
triggerListener: TriggerEventListener,
|
||||||
@@ -214,6 +232,22 @@ export const createLifecycleHub = (
|
|||||||
scrollTop(_viewport, scrollOffsetY);
|
scrollTop(_viewport, scrollOffsetY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
_viewportOverflowAmountCache: overflowAmountCache,
|
||||||
|
_viewportOverflowScrollCache: overflowScrollCache,
|
||||||
|
} = lifecycleCommunication;
|
||||||
|
const [overflowAmount, overflowAmountChanged, prevOverflowAmount] = overflowAmountCache;
|
||||||
|
const [overflowScroll, overflowScrollChanged, prevOverflowScroll] = overflowScrollCache;
|
||||||
|
|
||||||
|
if (overflowAmountChanged || overflowScrollChanged) {
|
||||||
|
triggerListener(
|
||||||
|
'overflowChanged',
|
||||||
|
assignDeep({}, createOverflowChangedArgs(overflowAmount, overflowScroll), {
|
||||||
|
previous: createOverflowChangedArgs(prevOverflowAmount!, prevOverflowScroll!),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
triggerListener('updated', {
|
triggerListener('updated', {
|
||||||
updateHints: {
|
updateHints: {
|
||||||
sizeChanged: adaptivedUpdateHints._sizeChanged,
|
sizeChanged: adaptivedUpdateHints._sizeChanged,
|
||||||
@@ -237,7 +271,7 @@ export const createLifecycleHub = (
|
|||||||
return {
|
return {
|
||||||
_update: update,
|
_update: update,
|
||||||
_state: () => ({
|
_state: () => ({
|
||||||
_overflowAmount: lifecycleCommunication._viewportOverflowAmount,
|
_overflowAmount: lifecycleCommunication._viewportOverflowAmountCache[0],
|
||||||
}),
|
}),
|
||||||
_destroy() {
|
_destroy() {
|
||||||
destroyObservers();
|
destroyObservers();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
getBoundingClientRect,
|
getBoundingClientRect,
|
||||||
noop,
|
noop,
|
||||||
each,
|
each,
|
||||||
|
equalXY,
|
||||||
} from 'support';
|
} from 'support';
|
||||||
import { LifecycleHub, Lifecycle } from 'lifecycles/lifecycleHub';
|
import { LifecycleHub, Lifecycle } from 'lifecycles/lifecycleHub';
|
||||||
import { getEnvironment } from 'environment';
|
import { getEnvironment } from 'environment';
|
||||||
@@ -28,11 +29,6 @@ interface ViewportOverflowState {
|
|||||||
_overflowScroll: XY<boolean>;
|
_overflowScroll: XY<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OverflowOption {
|
|
||||||
x: OverflowBehavior;
|
|
||||||
y: OverflowBehavior;
|
|
||||||
}
|
|
||||||
|
|
||||||
type UndoViewportArrangeResult = [
|
type UndoViewportArrangeResult = [
|
||||||
() => void, // redoViewportArrange
|
() => void, // redoViewportArrange
|
||||||
ViewportOverflowState?
|
ViewportOverflowState?
|
||||||
@@ -44,6 +40,10 @@ const whCacheOptions = {
|
|||||||
_equal: equalWH,
|
_equal: equalWH,
|
||||||
_initialValue: { w: 0, h: 0 },
|
_initialValue: { w: 0, h: 0 },
|
||||||
};
|
};
|
||||||
|
const xyCacheOptions = {
|
||||||
|
_equal: equalXY,
|
||||||
|
_initialValue: { x: false, y: false },
|
||||||
|
};
|
||||||
const sizeFraction = (elm: HTMLElement): WH<number> => {
|
const sizeFraction = (elm: HTMLElement): WH<number> => {
|
||||||
const viewportOffsetSize = offsetSize(elm);
|
const viewportOffsetSize = offsetSize(elm);
|
||||||
const viewportRect = getBoundingClientRect(elm);
|
const viewportRect = getBoundingClientRect(elm);
|
||||||
@@ -126,6 +126,8 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
const [updateOverflowAmountCache, getCurrentOverflowAmountCache] =
|
const [updateOverflowAmountCache, getCurrentOverflowAmountCache] =
|
||||||
createCache<WH<number>>(whCacheOptions);
|
createCache<WH<number>>(whCacheOptions);
|
||||||
|
|
||||||
|
const [updateOverflowScrollCache] = createCache<XY<boolean>>(xyCacheOptions);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies a fixed height to the viewport so it can't overflow or underflow the host element.
|
* Applies a fixed height to the viewport so it can't overflow or underflow the host element.
|
||||||
* @param viewportOverflowState The current overflow state.
|
* @param viewportOverflowState The current overflow state.
|
||||||
@@ -215,7 +217,7 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
const setViewportOverflowState = (
|
const setViewportOverflowState = (
|
||||||
showNativeOverlaidScrollbars: boolean,
|
showNativeOverlaidScrollbars: boolean,
|
||||||
overflowAmount: WH<number>,
|
overflowAmount: WH<number>,
|
||||||
overflow: OverflowOption,
|
overflow: XY<OverflowBehavior>,
|
||||||
viewportStyleObj: StyleObject
|
viewportStyleObj: StyleObject
|
||||||
): ViewportOverflowState => {
|
): ViewportOverflowState => {
|
||||||
const { _visible: xVisible, _behavior: xVisibleBehavior } = setAxisOverflowStyle(
|
const { _visible: xVisible, _behavior: xVisibleBehavior } = setAxisOverflowStyle(
|
||||||
@@ -514,7 +516,7 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
const [viewportSizeFraction, viewportSizeFractionChanged] = viewportSizeFractionCache;
|
const [viewportSizeFraction, viewportSizeFractionChanged] = viewportSizeFractionCache;
|
||||||
const [viewportScrollSize, viewportScrollSizeChanged] = viewportScrollSizeCache;
|
const [viewportScrollSize, viewportScrollSizeChanged] = viewportScrollSizeCache;
|
||||||
const [overflowAmount, overflowAmountChanged] = overflowAmuntCache;
|
const [overflowAmount, overflowAmountChanged] = overflowAmuntCache;
|
||||||
const [overflow, overflowChanged] = checkOption<OverflowOption>('overflow');
|
const [overflow, overflowChanged] = checkOption<XY<OverflowBehavior>>('overflow');
|
||||||
|
|
||||||
if (
|
if (
|
||||||
_paddingStyleChanged ||
|
_paddingStyleChanged ||
|
||||||
@@ -561,8 +563,10 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
|||||||
style(_viewport, viewportStyle);
|
style(_viewport, viewportStyle);
|
||||||
|
|
||||||
_setLifecycleCommunication({
|
_setLifecycleCommunication({
|
||||||
_viewportOverflowScroll: viewportOverflowState._overflowScroll,
|
_viewportOverflowScrollCache: updateOverflowScrollCache(
|
||||||
_viewportOverflowAmount: overflowAmount,
|
viewportOverflowState._overflowScroll
|
||||||
|
),
|
||||||
|
_viewportOverflowAmountCache: overflowAmuntCache,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ export interface CacheOptions<Value> {
|
|||||||
export type CacheValues<T> = [
|
export type CacheValues<T> = [
|
||||||
T, // value
|
T, // value
|
||||||
boolean, // changed
|
boolean, // changed
|
||||||
T | undefined // previous
|
T? // previous
|
||||||
];
|
];
|
||||||
|
|
||||||
export type EqualCachePropFunction<Value> = (currentVal: Value, newVal: Value) => boolean;
|
export type EqualCachePropFunction<Value> = (currentVal: Value, newVal: Value) => boolean;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const equal = <T extends PlainObject>(
|
|||||||
* @param a Object a.
|
* @param a Object a.
|
||||||
* @param b Object b.
|
* @param b Object b.
|
||||||
*/
|
*/
|
||||||
export const equalWH = (a?: WH, b?: WH) => equal<WH>(a, b, ['w', 'h']);
|
export const equalWH = <T>(a?: WH<T>, b?: WH<T>) => equal<WH<T>>(a, b, ['w', 'h']);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares object a with object b and returns true if both have the same property values, false otherwise.
|
* Compares object a with object b and returns true if both have the same property values, false otherwise.
|
||||||
@@ -42,7 +42,7 @@ export const equalWH = (a?: WH, b?: WH) => equal<WH>(a, b, ['w', 'h']);
|
|||||||
* @param a Object a.
|
* @param a Object a.
|
||||||
* @param b Object b.
|
* @param b Object b.
|
||||||
*/
|
*/
|
||||||
export const equalXY = (a?: XY, b?: XY) => equal<XY>(a, b, ['x', 'y']);
|
export const equalXY = <T>(a?: XY<T>, b?: XY<T>) => equal<XY<T>>(a, b, ['x', 'y']);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares object a with object b and returns true if both have the same property values, false otherwise.
|
* Compares object a with object b and returns true if both have the same property values, false otherwise.
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ export const debounce = <FunctionToDebounce extends (...args: any) => any>(
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
clearTimeouts(timeoutId);
|
clearTimeouts(timeoutId);
|
||||||
|
// @ts-ignore
|
||||||
timeoutId = setTimeoutFn(boundInvoke, finalTimeout as number) as number;
|
timeoutId = setTimeoutFn(boundInvoke, finalTimeout as number) as number;
|
||||||
|
|
||||||
if (hasMaxWait && !maxTimeoutId) {
|
if (hasMaxWait && !maxTimeoutId) {
|
||||||
|
|||||||
+40
-9
@@ -89,7 +89,21 @@ type OSPlugin<T extends OSPluginInstance> = [
|
|||||||
string,
|
string,
|
||||||
T
|
T
|
||||||
];
|
];
|
||||||
interface onUpdatedEventArgs {
|
interface XY<T> {
|
||||||
|
x: T;
|
||||||
|
y: T;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
onScrollStart : null,
|
||||||
|
onScroll : null,
|
||||||
|
onScrollStop : null,
|
||||||
|
onOverflowChanged : null,
|
||||||
|
onOverflowAmountChanged : null, // fusion with onOverflowChanged
|
||||||
|
onDirectionChanged : null, // gone
|
||||||
|
onContentSizeChanged : null, // gone
|
||||||
|
onHostSizeChanged : null, // gone
|
||||||
|
*/
|
||||||
|
interface OnUpdatedEventListenerArgs {
|
||||||
updateHints: {
|
updateHints: {
|
||||||
sizeChanged: boolean;
|
sizeChanged: boolean;
|
||||||
hostMutation: boolean;
|
hostMutation: boolean;
|
||||||
@@ -100,14 +114,31 @@ interface onUpdatedEventArgs {
|
|||||||
changedOptions: PartialOptions<OSOptions>;
|
changedOptions: PartialOptions<OSOptions>;
|
||||||
force: boolean;
|
force: boolean;
|
||||||
}
|
}
|
||||||
interface EventArgsMap {
|
interface OnOverflowChangedEventListenerArgs {
|
||||||
updated: onUpdatedEventArgs;
|
overflow: XY<boolean>; // whether there is an overflow
|
||||||
|
scrollableOverflow: XY<boolean>; // whether there is an scrollable overflow
|
||||||
|
amount: XY<number>; // the overflow amount in pixel
|
||||||
|
previous: {
|
||||||
|
overflow: XY<boolean>;
|
||||||
|
scrollableOverflow: XY<boolean>;
|
||||||
|
amount: XY<number>;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
type OSEventListener<N extends keyof EventArgsMap> = (args: EventArgsMap[N]) => void;
|
interface EventListenerArgsMap {
|
||||||
type AddEvent = <N extends keyof EventArgsMap>(name: N, listener: OSEventListener<N> | OSEventListener<N>[]) => () => void;
|
initialized: false;
|
||||||
type RemoveEvent = <N extends keyof EventArgsMap>(name?: N, listener?: OSEventListener<N> | OSEventListener<N>[]) => void;
|
initializationWithdrawn: false;
|
||||||
|
overflowChanged: OnOverflowChangedEventListenerArgs;
|
||||||
|
updated: OnUpdatedEventListenerArgs;
|
||||||
|
destroyed: false;
|
||||||
|
}
|
||||||
|
type OSEventListener<N extends keyof EventListenerArgsMap> = (args: EventListenerArgsMap[N]) => void;
|
||||||
|
type AddEventListener = <N extends keyof EventListenerArgsMap>(name: N, listener: OSEventListener<N> | OSEventListener<N>[]) => () => void;
|
||||||
|
type RemoveEventListener = <N extends keyof EventListenerArgsMap>(name?: N, listener?: OSEventListener<N> | OSEventListener<N>[]) => void;
|
||||||
|
type EventListenersMap = {
|
||||||
|
[K in keyof EventListenerArgsMap]?: OSEventListener<K> | OSEventListener<K>[];
|
||||||
|
};
|
||||||
interface OverlayScrollbarsStatic {
|
interface OverlayScrollbarsStatic {
|
||||||
(target: OSTarget | OSInitializationObject, options?: PartialOptions<OSOptions>, extensions?: any): OverlayScrollbars;
|
(target: OSTarget | OSInitializationObject, options?: PartialOptions<OSOptions>, eventListeners?: EventListenersMap): OverlayScrollbars;
|
||||||
extend(osPlugin: OSPlugin | OSPlugin[]): void;
|
extend(osPlugin: OSPlugin | OSPlugin[]): void;
|
||||||
}
|
}
|
||||||
interface OverlayScrollbars {
|
interface OverlayScrollbars {
|
||||||
@@ -116,8 +147,8 @@ interface OverlayScrollbars {
|
|||||||
update(force?: boolean): void;
|
update(force?: boolean): void;
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
state(): any;
|
state(): any;
|
||||||
on: AddEvent;
|
on: AddEventListener;
|
||||||
off: RemoveEvent;
|
off: RemoveEventListener;
|
||||||
}
|
}
|
||||||
declare const OverlayScrollbars: OverlayScrollbarsStatic;
|
declare const OverlayScrollbars: OverlayScrollbarsStatic;
|
||||||
export { OverlayScrollbars as default };
|
export { OverlayScrollbars as default };
|
||||||
|
|||||||
Reference in New Issue
Block a user