improve code

This commit is contained in:
Rene
2021-04-28 22:04:12 +02:00
parent 970f69a5ab
commit 81a644c532
7 changed files with 183 additions and 160 deletions
@@ -1,5 +1,22 @@
import { XY, WH, TRBL, CacheValues, PartialOptions, each, push, keys, hasOwnProperty, isNumber, scrollLeft, scrollTop } from 'support';
import {
XY,
WH,
TRBL,
CacheValues,
PartialOptions,
each,
push,
keys,
hasOwnProperty,
isNumber,
scrollLeft,
scrollTop,
assignDeep,
liesBetween,
diffClass,
} from 'support';
import { OSOptions } from 'options';
import { classNameHost, classNameViewport, classNameContent } from 'classnames';
import { getEnvironment } from 'environment';
import { StructureSetup } from 'setups/structureSetup';
import { createTrinsicLifecycle } from 'lifecycles/trinsicLifecycle';
@@ -12,16 +29,21 @@ import { StyleObject } from 'typings';
export type LifecycleCheckOption = <T>(path: string) => LifecycleOptionInfo<T>;
export interface PaddingInfo {
_absolute: boolean;
_padding: TRBL;
}
export interface LifecycleOptionInfo<T> {
readonly _value: T;
_changed: boolean;
}
export interface LifecycleCommunication {
_paddingInfo: {
_absolute: boolean;
_padding: TRBL;
};
_viewportPaddingStyle: StyleObject;
_viewportOverflowScroll: XY<boolean>;
_viewportOverflowAmount: WH<number>;
}
export interface LifecycleAdaptiveUpdateHints {
_sizeChanged: boolean;
_hostMutation: boolean;
@@ -40,9 +62,13 @@ export type Lifecycle = (
force: boolean
) => Partial<LifecycleAdaptiveUpdateHints> | void;
export interface LifecycleHubState {
_overflowAmount: WH<number>;
}
export interface LifecycleHubInstance {
_update(changedOptions?: PartialOptions<OSOptions> | null, force?: boolean): void;
_state(): any;
_state(): LifecycleHubState;
_destroy(): void;
}
@@ -51,19 +77,12 @@ export interface LifecycleHub {
_structureSetup: StructureSetup;
// whether the "viewport arrange" strategy must be used (true if no native scrollbar hiding and scrollbars are overlaid)
_doViewportArrange: boolean;
_getPaddingInfo(): PaddingInfo;
_setPaddingInfo(newPadding?: PaddingInfo | null): void;
// padding related styles applied to the viewport element
_getViewportPaddingStyle(): StyleObject;
_setViewportPaddingStyle(newPaddingStlye?: StyleObject | null): void;
_getViewportOverflowScroll(): XY<boolean>;
_setViewportOverflowScroll(newViewportOverflowScroll?: XY<boolean>): void;
_getViewportOverflowAmount(): WH<number>;
_setViewportOverflowAmount(newViewportOverflowAmount?: WH<number>): void;
_getLifecycleCommunication(): LifecycleCommunication;
_setLifecycleCommunication(newLifecycleCommunication?: Partial<LifecycleCommunication>): void;
}
const getPropByPath = <T>(obj: any, path: string): T =>
obj && path.split('.').reduce((o, prop) => (o && hasOwnProperty(o, prop) ? o[prop] : undefined), obj);
obj ? path.split('.').reduce((o, prop) => (o && hasOwnProperty(o, prop) ? o[prop] : undefined), obj) : undefined;
const emptyStylePropsToZero = (stlyeObj: StyleObject, baseStyle?: StyleObject) =>
keys(stlyeObj).reduce(
@@ -77,33 +96,19 @@ const emptyStylePropsToZero = (stlyeObj: StyleObject, baseStyle?: StyleObject) =
// TODO: observer textarea attrs if textarea
// TODO: tabindex, open etc.
// TODO: test _ignoreContentChange & _ignoreNestedTargetChange for content dom observer
// TODO: test _ignoreTargetChange for target dom observer
const ignorePrefix = 'os-';
const hostSelector = `.${classNameHost}`;
const viewportSelector = `.${classNameViewport}`;
const contentSelector = `.${classNameContent}`;
const attrs = ['id', 'class', 'style', 'open'];
const paddingInfoFallback: PaddingInfo = {
_absolute: false,
_padding: {
t: 0,
r: 0,
b: 0,
l: 0,
},
};
const viewportPaddingStyleFallback: StyleObject = {
marginTop: 0,
marginRight: 0,
marginBottom: 0,
marginLeft: 0,
paddingTop: 0,
paddingRight: 0,
paddingBottom: 0,
paddingLeft: 0,
};
const viewportOverflowScrollFallback: XY<boolean> = {
x: false,
y: false,
};
const viewportOverflowAmountFallback: WH<number> = {
w: 0,
h: 0,
const ignoreTargetChange = (target: Node, attrName: string, oldValue: string | null, newValue: string | null) => {
if (attrName === 'class' && oldValue && newValue) {
const diff = diffClass(oldValue, newValue);
return !!diff.find((addedOrRemovedClass) => addedOrRemovedClass.indexOf(ignorePrefix) !== 0);
}
return false;
};
const directionIsRTLCacheValuesFallback: CacheValues<boolean> = {
_value: false,
@@ -115,12 +120,38 @@ const heightIntrinsicCacheValuesFallback: CacheValues<boolean> = {
_previous: false,
_changed: false,
};
const lifecycleCommunicationFallback: LifecycleCommunication = {
_paddingInfo: {
_absolute: false,
_padding: {
t: 0,
r: 0,
b: 0,
l: 0,
},
},
_viewportOverflowScroll: {
x: false,
y: false,
},
_viewportOverflowAmount: {
w: 0,
h: 0,
},
_viewportPaddingStyle: {
marginTop: 0,
marginRight: 0,
marginBottom: 0,
marginLeft: 0,
paddingTop: 0,
paddingRight: 0,
paddingBottom: 0,
paddingLeft: 0,
},
};
export const createLifecycleHub = (options: OSOptions, structureSetup: StructureSetup): LifecycleHubInstance => {
let paddingInfo = paddingInfoFallback;
let viewportPaddingStyle = viewportPaddingStyleFallback;
let viewportOverflowScroll = viewportOverflowScrollFallback;
let viewportOverflowAmount = viewportOverflowAmountFallback;
let lifecycleCommunication = lifecycleCommunicationFallback;
const { _host, _viewport, _content } = structureSetup._targetObj;
const {
_nativeScrollbarStyling,
@@ -135,21 +166,16 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
_options: options,
_structureSetup: structureSetup,
_doViewportArrange: doViewportArrange,
_getPaddingInfo: () => paddingInfo,
_setPaddingInfo(newPaddingInfo) {
paddingInfo = newPaddingInfo || paddingInfoFallback;
},
_getViewportPaddingStyle: () => viewportPaddingStyle,
_setViewportPaddingStyle(newPaddingStlye) {
viewportPaddingStyle = newPaddingStlye ? emptyStylePropsToZero(newPaddingStlye, viewportPaddingStyleFallback) : viewportPaddingStyleFallback;
},
_getViewportOverflowScroll: () => viewportOverflowScroll,
_setViewportOverflowScroll(newViewportOverflowScroll) {
viewportOverflowScroll = newViewportOverflowScroll || viewportOverflowScrollFallback;
},
_getViewportOverflowAmount: () => viewportOverflowAmount,
_setViewportOverflowAmount(newViewportOverflowAmount) {
viewportOverflowAmount = newViewportOverflowAmount || viewportOverflowAmountFallback;
_getLifecycleCommunication: () => lifecycleCommunication,
_setLifecycleCommunication(newLifecycleCommunication) {
if (newLifecycleCommunication && newLifecycleCommunication._viewportPaddingStyle) {
newLifecycleCommunication._viewportPaddingStyle = emptyStylePropsToZero(
newLifecycleCommunication._viewportPaddingStyle,
lifecycleCommunicationFallback._viewportPaddingStyle
);
}
lifecycleCommunication = assignDeep({}, lifecycleCommunication, newLifecycleCommunication);
},
};
@@ -172,7 +198,7 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
_heightIntrinsic || (trinsicObserver ? trinsicObserver._getCurrentCacheValues(force)._heightIntrinsic : heightIntrinsicCacheValuesFallback);
const checkOption: LifecycleCheckOption = (path) => ({
_value: getPropByPath(options, path),
_changed: force || (!!changedOptions && getPropByPath(changedOptions, path) !== undefined),
_changed: force || getPropByPath(changedOptions, path) !== undefined,
});
const adjustScrollOffset = doViewportArrange || !_flexboxGlue;
const scrollOffsetX = adjustScrollOffset && scrollLeft(_viewport);
@@ -246,26 +272,22 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
const hostMutationObserver = createDOMObserver(_host, false, onHostMutation, {
_styleChangingAttributes: attrs,
_attributes: attrs,
_ignoreTargetChange: ignoreTargetChange,
});
const contentMutationObserver = createDOMObserver(_content || _viewport, true, onContentMutation, {
_styleChangingAttributes: attrs,
_attributes: attrs,
_eventContentChange: options!.updating!.elementEvents as [string, string][],
/*
_nestedTargetSelector: hostSelector,
_ignoreContentChange: (mutation, isNestedTarget) => {
const { target, attributeName } = mutation;
return isNestedTarget ? false : attributeName ? liesBetween(target as Element, hostSelector, '.content') : false;
},
_ignoreTargetAttrChange: (target, attrName, oldValue, newValue) => {
if (attrName === 'class' && oldValue && newValue) {
const diff = diffClass(oldValue, newValue);
const ignore = diff.length === 1 && diff[0].startsWith(ignorePrefix);
return ignore;
}
return false;
},
*/
_eventContentChange: options!.updating!.elementEvents,
_nestedTargetSelector: hostSelector,
_ignoreContentChange: (mutation, isNestedTarget) => {
const { target, attributeName } = mutation;
return isNestedTarget
? false
: attributeName
? liesBetween(target as Element, hostSelector, viewportSelector) || liesBetween(target as Element, hostSelector, contentSelector)
: false;
},
_ignoreNestedTargetChange: ignoreTargetChange,
});
const update = (changedOptions?: Partial<OSOptions> | null, force?: boolean) => {
@@ -279,7 +301,7 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
return {
_update: update,
_state: () => ({
_overflowAmount: viewportOverflowAmount,
_overflowAmount: lifecycleCommunication._viewportOverflowAmount,
}),
_destroy() {
removeEnvironmentListener(envUpdateListener);
@@ -57,14 +57,7 @@ const overlaidScrollbarsHideOffset = 42;
* @returns
*/
export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => {
const {
_structureSetup,
_doViewportArrange,
_getViewportPaddingStyle,
_getPaddingInfo,
_setViewportOverflowScroll,
_setViewportOverflowAmount,
} = lifecycleHub;
const { _structureSetup, _doViewportArrange, _getLifecycleCommunication, _setLifecycleCommunication } = lifecycleHub;
const { _host, _viewport, _viewportArrange } = _structureSetup._targetObj;
const { _update: updateContentScrollSizeCache, _current: getCurrentContentScrollSizeCache } = createCache<
WH<number>,
@@ -101,7 +94,7 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
});
if (heightIntrinsic) {
const { _absolute: paddingAbsolute, _padding: padding } = _getPaddingInfo();
const { _absolute: paddingAbsolute, _padding: padding } = _getLifecycleCommunication()._paddingInfo;
const { _overflowScroll, _scrollbarsHideOffset } = viewportOverflowState;
const hostBCR = getBoundingClientRect(_host);
const hostOffsetSize = offsetSize(_host);
@@ -204,7 +197,7 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
const { _scrollbarsHideOffset, _scrollbarsHideOffsetArrange } = viewportOverflowState;
const { x: arrangeX, y: arrangeY } = _scrollbarsHideOffsetArrange;
const { x: hideOffsetX, y: hideOffsetY } = _scrollbarsHideOffset;
const viewportPaddingStyle = _getViewportPaddingStyle();
const { _viewportPaddingStyle: viewportPaddingStyle } = _getLifecycleCommunication();
const viewportArrangeHorizontalPaddingKey: keyof StyleObject = directionIsRTL ? 'paddingRight' : 'paddingLeft';
const viewportArrangeHorizontalPaddingValue = viewportPaddingStyle[viewportArrangeHorizontalPaddingKey] as number;
const viewportArrangeVerticalPaddingValue = viewportPaddingStyle.paddingTop as number;
@@ -257,13 +250,13 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
const { _scrollbarsHideOffset, _scrollbarsHideOffsetArrange } = viewportOverflowState;
const { x: arrangeX, y: arrangeY } = _scrollbarsHideOffsetArrange;
const { x: hideOffsetX, y: hideOffsetY } = _scrollbarsHideOffset;
const paddingStyle = _getViewportPaddingStyle();
const { _viewportPaddingStyle: viewportPaddingStyle } = _getLifecycleCommunication();
const horizontalMarginKey: keyof StyleObject = directionIsRTL ? 'marginLeft' : 'marginRight';
const viewportHorizontalPaddingKey: keyof StyleObject = directionIsRTL ? 'paddingLeft' : 'paddingRight';
const horizontalMarginValue = paddingStyle[horizontalMarginKey] as number;
const verticalMarginValue = paddingStyle.marginBottom as number;
const horizontalPaddingValue = paddingStyle[viewportHorizontalPaddingKey] as number;
const verticalPaddingValue = paddingStyle.paddingBottom as number;
const horizontalMarginValue = viewportPaddingStyle[horizontalMarginKey] as number;
const verticalMarginValue = viewportPaddingStyle.marginBottom as number;
const horizontalPaddingValue = viewportPaddingStyle[viewportHorizontalPaddingKey] as number;
const verticalPaddingValue = viewportPaddingStyle.paddingBottom as number;
// horizontal
viewportStyleObj.maxWidth = `calc(100% + ${hideOffsetY + horizontalMarginValue * -1}px)`;
@@ -293,14 +286,14 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
): UndoViewportArrangeResult => {
if (_doViewportArrange) {
const finalViewportOverflowState = viewportOverflowState || getViewportOverflowState(showNativeOverlaidScrollbars);
const paddingStyle = _getViewportPaddingStyle();
const { _viewportPaddingStyle: viewportPaddingStyle } = _getLifecycleCommunication();
const { _flexboxGlue } = getEnvironment();
const { _scrollbarsHideOffsetArrange } = finalViewportOverflowState;
const { x: arrangeX, y: arrangeY } = _scrollbarsHideOffsetArrange;
const finalPaddingStyle: StyleObject = {};
const assignProps = (props: string) =>
each(props.split(' '), (prop) => {
finalPaddingStyle[prop] = paddingStyle[prop];
finalPaddingStyle[prop] = viewportPaddingStyle[prop];
});
if (!_flexboxGlue) {
@@ -443,8 +436,10 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
style(_viewport, viewportStyle);
_setViewportOverflowScroll(viewportOverflowState._overflowScroll);
_setViewportOverflowAmount(overflowAmount);
_setLifecycleCommunication({
_viewportOverflowScroll: viewportOverflowState._overflowScroll,
_viewportOverflowAmount: overflowAmount,
});
}
};
};
@@ -9,7 +9,7 @@ import { getEnvironment } from 'environment';
* @returns
*/
export const createPaddingLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => {
const { _setPaddingInfo, _setViewportPaddingStyle, _structureSetup } = lifecycleHub;
const { _structureSetup, _setLifecycleCommunication } = lifecycleHub;
const { _host, _padding, _viewport } = _structureSetup._targetObj;
const { _update: updatePaddingCache, _current: currentPaddingCache } = createCache(() => topRightBottomLeft(_host, 'padding'), {
_equal: equalTRBL,
@@ -66,18 +66,18 @@ export const createPaddingLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =>
style(_padding || _viewport, paddingStyle);
style(_viewport, viewportStyle);
_setPaddingInfo({
_absolute: !paddingRelative,
_padding: padding!,
});
_setViewportPaddingStyle(
_padding
_setLifecycleCommunication({
_paddingInfo: {
_absolute: !paddingRelative,
_padding: padding!,
},
_viewportPaddingStyle: _padding
? viewportStyle
: {
...paddingStyle,
...viewportStyle,
}
);
},
});
}
return {
@@ -49,11 +49,9 @@ interface DOMContentObserver extends DOMObserverBase {
interface DOMTargetObserver extends DOMObserverBase {}
export type DOMObserverEventContentChange =
| Array<[StringNullUndefined, ((elms: Node[]) => StringNullUndefined) | StringNullUndefined] | null | undefined>
| false
| null
| undefined;
type ContentChangeArrayItem = [StringNullUndefined, ((elms: Node[]) => StringNullUndefined) | StringNullUndefined] | null | undefined;
export type DOMObserverEventContentChange = Array<ContentChangeArrayItem> | false | null | undefined;
export type DOMObserverIgnoreContentChange = (
mutation: MutationRecord,
+2 -2
View File
@@ -34,7 +34,7 @@ export interface OSOptions {
resize: ResizeBehavior;
paddingAbsolute: boolean;
updating: {
elementEvents: ReadonlyArray<[string, string]> | null;
elementEvents: Array<[string, string]> | null;
contentMutationDebounce: number;
hostMutationDebounce: number;
resizeDebounce: number;
@@ -54,7 +54,7 @@ export interface OSOptions {
textarea: {
dynWidth: boolean;
dynHeight: boolean;
inheritedAttrs: string | ReadonlyArray<string> | null;
inheritedAttrs: string | Array<string> | null;
};
nativeScrollbarsOverlaid: {
show: boolean;
@@ -12,6 +12,9 @@ import { clientSize, from, getBoundingClientRect, style, parent, addClass, WH, r
const msie11 = !!window.MSInputMethodContext && !!document.documentMode;
const firefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
msie11 && addClass(document.body, 'msie11');
firefox && addClass(document.body, 'firefox');
const useContentElement = false;
const fixedDigits = msie11 ? 1 : 10;
const fixedDigitsOffset = firefox ? 3 : fixedDigits; // ff does roundign errors here only
@@ -220,27 +223,6 @@ const iterateMinMax = async (afterEach?: () => any) => {
};
const containerTest = async () => {
await iterateMinMax(async () => {
await iterateBoxSizing(async () => {
await iterateHeight(async () => {
await iterateWidth(async () => {
await iterateBorder(async () => {
// assume this part isn't critical for IE11, to boost test speed
if (!msie11) {
await iterateFloat(async () => {
await iterateMargin();
});
}
await iteratePadding();
await iterateDirection();
});
});
});
});
});
};
const overflowTest = async () => {
const contentBox = (elm: HTMLElement | null): WH<number> => {
if (elm) {
const computedStyle = window.getComputedStyle(elm);
@@ -323,50 +305,66 @@ const overflowTest = async () => {
await checkMetrics();
};
const overflowTest = async () => {
style(targetResize, { boxSizing: 'border-box' });
style(comparisonResize, { boxSizing: 'border-box' });
style(targetPercent, { display: 'none' });
style(comparisonPercent, { display: 'none' });
style(targetEnd, { display: 'none' });
style(comparisonEnd, { display: 'none' });
style(targetResize, { boxSizing: 'border-box', background: 'rgba(0, 0, 0, 0.1)' });
style(comparisonResize, { boxSizing: 'border-box', background: 'rgba(0, 0, 0, 0.1)' });
style(targetPercent, { display: 'none' });
style(comparisonPercent, { display: 'none' });
style(targetEnd, { display: 'none' });
style(comparisonEnd, { display: 'none' });
await setNoOverflow();
await setSmallestOverflow(true, false);
await setSmallestOverflow(false, true);
await setSmallestOverflow(true, true);
await setNoOverflow();
await setLargeOverflow(true, false);
await setLargeOverflow(false, true);
await setLargeOverflow(true, true);
removeAttr(targetResize, 'style');
removeAttr(comparisonResize, 'style');
removeAttr(targetPercent, 'style');
removeAttr(comparisonPercent, 'style');
removeAttr(targetEnd, 'style');
removeAttr(comparisonEnd, 'style');
if (msie11) {
await timeout(20);
}
await checkMetrics();
};
await iterateMinMax(async () => {
await iterateBoxSizing(async () => {
await iterateHeight(async () => {
await iterateWidth(async () => {
await iterateBorder(async () => {
await iteratePadding(async () => {
await setNoOverflow();
await setSmallestOverflow(true, false);
await setSmallestOverflow(false, true);
await setSmallestOverflow(true, true);
// assume this part isn't critical for IE11, to boost test speed
if (!msie11) {
await iterateFloat(async () => {
await iterateMargin();
});
}
await setNoOverflow();
await setLargeOverflow(true, false);
await setLargeOverflow(false, true);
await setLargeOverflow(true, true);
await iteratePadding(async () => {
await overflowTest();
});
await iterateDirection();
});
});
});
});
});
removeAttr(targetResize, 'style');
removeAttr(comparisonResize, 'style');
removeAttr(targetPercent, 'style');
removeAttr(comparisonPercent, 'style');
removeAttr(targetEnd, 'style');
removeAttr(comparisonEnd, 'style');
};
const start = async () => {
setTestResult(null);
target?.removeAttribute('style');
//await containerTest();
await overflowTest();
await containerTest();
setTestResult(true);
};
@@ -246,6 +246,16 @@ body {
max-width: none;
}
// ie11 doesn't respect percentage max-width in display: inline-block elements, they can always expand to infinity
.msie11 {
.column {
min-width: 900px;
}
.minMaxNone.widthAuto > .container {
max-width: 700px;
}
}
.intrinsic-hack {
&.heightAuto,
&.envHeightAuto {