mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-11 20:42:27 +03:00
add structureSetup
This commit is contained in:
@@ -8,7 +8,7 @@ import {
|
|||||||
hasOwnProperty,
|
hasOwnProperty,
|
||||||
isEmptyObject,
|
isEmptyObject,
|
||||||
} from 'support';
|
} from 'support';
|
||||||
import { CSSDirection, PlainObject } from 'typings';
|
import { PlainObject } from 'typings';
|
||||||
|
|
||||||
interface LifecycleBaseUpdateHints<O> {
|
interface LifecycleBaseUpdateHints<O> {
|
||||||
_force?: boolean;
|
_force?: boolean;
|
||||||
@@ -23,7 +23,7 @@ export interface LifecycleBase<O extends PlainObject> {
|
|||||||
export interface Lifecycle<T extends PlainObject> extends LifecycleBase<T> {
|
export interface Lifecycle<T extends PlainObject> extends LifecycleBase<T> {
|
||||||
_destruct(): void;
|
_destruct(): void;
|
||||||
_onSizeChanged?(): void;
|
_onSizeChanged?(): void;
|
||||||
_onDirectionChanged?(directionCache: Cache<CSSDirection>): void;
|
_onDirectionChanged?(directionCache: Cache<boolean>): void;
|
||||||
_onTrinsicChanged?(widthIntrinsic: boolean, heightIntrinsicCache: Cache<boolean>): void;
|
_onTrinsicChanged?(widthIntrinsic: boolean, heightIntrinsicCache: Cache<boolean>): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
scrollSize,
|
scrollSize,
|
||||||
offsetSize,
|
offsetSize,
|
||||||
} from 'support';
|
} from 'support';
|
||||||
import { OSTargetObject } from 'typings';
|
import { PreparedOSTargetObject } from 'setups/structureSetup';
|
||||||
import { createLifecycleBase, Lifecycle } from 'lifecycles/lifecycleBase';
|
import { createLifecycleBase, Lifecycle } from 'lifecycles/lifecycleBase';
|
||||||
import { getEnvironment, Environment } from 'environment';
|
import { getEnvironment, Environment } from 'environment';
|
||||||
|
|
||||||
@@ -42,10 +42,10 @@ const cssMarginEnd = cssProperty('margin-inline-end');
|
|||||||
const cssBorderEnd = cssProperty('border-inline-end');
|
const cssBorderEnd = cssProperty('border-inline-end');
|
||||||
|
|
||||||
export const createStructureLifecycle = (
|
export const createStructureLifecycle = (
|
||||||
target: OSTargetObject,
|
target: PreparedOSTargetObject,
|
||||||
initialOptions?: StructureLifecycleOptions
|
initialOptions?: StructureLifecycleOptions
|
||||||
): Lifecycle<StructureLifecycleOptions> => {
|
): Lifecycle<StructureLifecycleOptions> => {
|
||||||
const { host, padding: paddingElm, viewport, content } = target;
|
const { _host, _padding, _viewport, _content } = target;
|
||||||
const destructFns: (() => any)[] = [];
|
const destructFns: (() => any)[] = [];
|
||||||
const env: Environment = getEnvironment();
|
const env: Environment = getEnvironment();
|
||||||
const scrollbarsOverlaid = env._nativeScrollbarIsOverlaid;
|
const scrollbarsOverlaid = env._nativeScrollbarIsOverlaid;
|
||||||
@@ -54,7 +54,7 @@ export const createStructureLifecycle = (
|
|||||||
// direction change is only needed to update scrollbar hiding, therefore its not needed if css can do it, scrollbars are invisible or overlaid on y axis
|
// direction change is only needed to update scrollbar hiding, therefore its not needed if css can do it, scrollbars are invisible or overlaid on y axis
|
||||||
const directionObserverObsolete = (cssMarginEnd && cssBorderEnd) || supportsScrollbarStyling || scrollbarsOverlaid.y;
|
const directionObserverObsolete = (cssMarginEnd && cssBorderEnd) || supportsScrollbarStyling || scrollbarsOverlaid.y;
|
||||||
|
|
||||||
const updatePaddingCache = createCache(() => topRightBottomLeft(host, 'padding'), { _equal: equalTRBL });
|
const updatePaddingCache = createCache(() => topRightBottomLeft(_host, 'padding'), { _equal: equalTRBL });
|
||||||
const updateOverflowAmountCache = createCache<XY<number>, { _contentScrollSize: WH<number>; _viewportSize: WH<number> }>(
|
const updateOverflowAmountCache = createCache<XY<number>, { _contentScrollSize: WH<number>; _viewportSize: WH<number> }>(
|
||||||
(ctx) => ({
|
(ctx) => ({
|
||||||
x: Math.max(0, Math.round((ctx!._contentScrollSize.w - ctx!._viewportSize.w) * 100) / 100),
|
x: Math.max(0, Math.round((ctx!._contentScrollSize.w - ctx!._viewportSize.w) * 100) / 100),
|
||||||
@@ -82,7 +82,7 @@ export const createStructureLifecycle = (
|
|||||||
paddingStyle.l = -padding!.l;
|
paddingStyle.l = -padding!.l;
|
||||||
}
|
}
|
||||||
|
|
||||||
style(paddingElm, {
|
style(_padding, {
|
||||||
top: paddingStyle.t,
|
top: paddingStyle.t,
|
||||||
left: paddingStyle.l,
|
left: paddingStyle.l,
|
||||||
'margin-right': paddingStyle.r,
|
'margin-right': paddingStyle.r,
|
||||||
@@ -91,9 +91,9 @@ export const createStructureLifecycle = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewportOffsetSize = offsetSize(paddingElm);
|
const viewportOffsetSize = offsetSize(_padding);
|
||||||
const contentClientSize = offsetSize(content);
|
const contentClientSize = offsetSize(_content);
|
||||||
const contentScrollSize = scrollSize(content);
|
const contentScrollSize = scrollSize(_content);
|
||||||
const overflowAmuntCache = updateOverflowAmountCache(force, {
|
const overflowAmuntCache = updateOverflowAmountCache(force, {
|
||||||
_contentScrollSize: contentScrollSize,
|
_contentScrollSize: contentScrollSize,
|
||||||
_viewportSize: {
|
_viewportSize: {
|
||||||
@@ -151,7 +151,7 @@ export const createStructureLifecycle = (
|
|||||||
const onTrinsicChanged = (widthIntrinsic: boolean, heightIntrinsicCache: Cache<boolean>) => {
|
const onTrinsicChanged = (widthIntrinsic: boolean, heightIntrinsicCache: Cache<boolean>) => {
|
||||||
const { _changed, _value } = heightIntrinsicCache;
|
const { _changed, _value } = heightIntrinsicCache;
|
||||||
if (_changed) {
|
if (_changed) {
|
||||||
style(content, { height: _value ? 'auto' : '100%' });
|
style(_content, { height: _value ? 'auto' : '100%' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,54 +1,21 @@
|
|||||||
import { OSTarget, OSTargetObject, CSSDirection } from 'typings';
|
import { OSTarget, OSTargetObject } from 'typings';
|
||||||
import { createStructureLifecycle } from 'lifecycles/structureLifecycle';
|
import { createStructureLifecycle } from 'lifecycles/structureLifecycle';
|
||||||
import { Cache, appendChildren, addClass, contents, is, isHTMLElement, createDiv, each, push } from 'support';
|
import { Cache, each, push } from 'support';
|
||||||
import { createSizeObserver } from 'observers/sizeObserver';
|
import { createSizeObserver } from 'observers/sizeObserver';
|
||||||
import { createTrinsicObserver } from 'observers/trinsicObserver';
|
import { createTrinsicObserver } from 'observers/trinsicObserver';
|
||||||
import { createDOMObserver } from 'observers/domObserver';
|
import { createDOMObserver } from 'observers/domObserver';
|
||||||
|
import { createStructureSetup, StructureSetup } from 'setups/structureSetup';
|
||||||
import { Lifecycle } from 'lifecycles/lifecycleBase';
|
import { Lifecycle } from 'lifecycles/lifecycleBase';
|
||||||
import { classNameHost, classNamePadding, classNameViewport, classNameContent } from 'classnames';
|
|
||||||
|
|
||||||
const normalizeTarget = (target: OSTarget): OSTargetObject => {
|
const OverlayScrollbars = (target: OSTarget | OSTargetObject, options?: any, extensions?: any): void => {
|
||||||
if (isHTMLElement(target)) {
|
const structureSetup: StructureSetup = createStructureSetup(target);
|
||||||
const isTextarea = is(target, 'textarea');
|
|
||||||
const host = (isTextarea ? createDiv() : target) as HTMLElement;
|
|
||||||
const padding = createDiv(classNamePadding);
|
|
||||||
const viewport = createDiv(classNameViewport);
|
|
||||||
const content = createDiv(classNameContent);
|
|
||||||
|
|
||||||
appendChildren(padding, viewport);
|
|
||||||
appendChildren(viewport, content);
|
|
||||||
appendChildren(content, contents(target));
|
|
||||||
appendChildren(target, padding);
|
|
||||||
addClass(host, classNameHost);
|
|
||||||
|
|
||||||
return {
|
|
||||||
target,
|
|
||||||
host,
|
|
||||||
padding,
|
|
||||||
viewport,
|
|
||||||
content,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const { host, padding, viewport, content } = target;
|
|
||||||
|
|
||||||
addClass(host, classNameHost);
|
|
||||||
addClass(padding, classNamePadding);
|
|
||||||
addClass(viewport, classNameViewport);
|
|
||||||
addClass(content, classNameContent);
|
|
||||||
|
|
||||||
return target;
|
|
||||||
};
|
|
||||||
|
|
||||||
const OverlayScrollbars = (target: OSTarget, options?: any, extensions?: any): void => {
|
|
||||||
const osTarget: OSTargetObject = normalizeTarget(target);
|
|
||||||
const lifecycles: Lifecycle<any>[] = [];
|
const lifecycles: Lifecycle<any>[] = [];
|
||||||
const { host, content } = osTarget;
|
const { _host, _viewport, _content } = structureSetup._targetObj;
|
||||||
|
|
||||||
push(lifecycles, createStructureLifecycle(osTarget));
|
push(lifecycles, createStructureLifecycle(structureSetup._targetObj));
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const onSizeChanged = (directionCache?: Cache<CSSDirection>) => {
|
const onSizeChanged = (directionCache?: Cache<boolean>) => {
|
||||||
if (directionCache) {
|
if (directionCache) {
|
||||||
each(lifecycles, (lifecycle) => {
|
each(lifecycles, (lifecycle) => {
|
||||||
lifecycle._onDirectionChanged && lifecycle._onDirectionChanged(directionCache);
|
lifecycle._onDirectionChanged && lifecycle._onDirectionChanged(directionCache);
|
||||||
@@ -65,13 +32,13 @@ const OverlayScrollbars = (target: OSTarget, options?: any, extensions?: any): v
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
createSizeObserver(host, onSizeChanged, { _appear: true, _direction: true });
|
createSizeObserver(_host, onSizeChanged, { _appear: true, _direction: true });
|
||||||
createTrinsicObserver(host, onTrinsicChanged);
|
createTrinsicObserver(_host, onTrinsicChanged);
|
||||||
createDOMObserver(host, () => {
|
createDOMObserver(_host, () => {
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
createDOMObserver(
|
createDOMObserver(
|
||||||
content,
|
_content || _viewport,
|
||||||
() => {
|
() => {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import {
|
||||||
|
isHTMLElement,
|
||||||
|
appendChildren,
|
||||||
|
is,
|
||||||
|
createDiv,
|
||||||
|
contents,
|
||||||
|
insertAfter,
|
||||||
|
addClass,
|
||||||
|
parent,
|
||||||
|
isUndefined,
|
||||||
|
removeElements,
|
||||||
|
removeClass,
|
||||||
|
push,
|
||||||
|
runEach,
|
||||||
|
} from 'support';
|
||||||
|
import { classNameHost, classNamePadding, classNameViewport, classNameContent } from 'classnames';
|
||||||
|
import { OSTarget, OSTargetObject, InternalVersionOf, OSTargetElement } from 'typings';
|
||||||
|
|
||||||
|
export interface OSTargetContext {
|
||||||
|
_isTextarea: boolean;
|
||||||
|
_isBody: boolean;
|
||||||
|
_htmlElm: HTMLHtmlElement;
|
||||||
|
_bodyElm: HTMLBodyElement;
|
||||||
|
_windowElm: Window;
|
||||||
|
_documentElm: HTMLDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PreparedOSTargetObject extends Required<InternalVersionOf<OSTargetObject>> {
|
||||||
|
_host: HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StructureSetup {
|
||||||
|
_targetObj: PreparedOSTargetObject;
|
||||||
|
_targetCtx: OSTargetContext;
|
||||||
|
_destroy: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unwrap = (elm: HTMLElement | null | undefined) => {
|
||||||
|
appendChildren(parent(elm), contents(elm));
|
||||||
|
removeElements(elm);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createStructureSetup = (target: OSTarget | OSTargetObject): StructureSetup => {
|
||||||
|
const targetIsElm = isHTMLElement(target);
|
||||||
|
const osTargetObj: InternalVersionOf<OSTargetObject> = targetIsElm
|
||||||
|
? ({} as InternalVersionOf<OSTargetObject>)
|
||||||
|
: {
|
||||||
|
_host: (target as OSTargetObject).host,
|
||||||
|
_target: (target as OSTargetObject).target,
|
||||||
|
_padding: (target as OSTargetObject).padding,
|
||||||
|
_viewport: (target as OSTargetObject).viewport,
|
||||||
|
_content: (target as OSTargetObject).content,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (targetIsElm) {
|
||||||
|
const padding = createDiv(classNamePadding);
|
||||||
|
const viewport = createDiv(classNameViewport);
|
||||||
|
const content = createDiv(classNameContent);
|
||||||
|
|
||||||
|
appendChildren(padding, viewport);
|
||||||
|
appendChildren(viewport, content);
|
||||||
|
|
||||||
|
osTargetObj._target = target as OSTargetElement;
|
||||||
|
osTargetObj._padding = padding;
|
||||||
|
osTargetObj._viewport = viewport;
|
||||||
|
osTargetObj._content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { _target, _padding, _viewport, _content } = osTargetObj;
|
||||||
|
let destroyFns: (() => any)[] = [];
|
||||||
|
const isTextarea = is(_target, 'textarea');
|
||||||
|
const isBody = !isTextarea && is(_target, 'body');
|
||||||
|
const _host = (isTextarea ? osTargetObj._host || createDiv() : _target) as HTMLElement;
|
||||||
|
const getTargetContents = (contentSlot: HTMLElement) => (isTextarea ? (_target as HTMLTextAreaElement) : contents(contentSlot as HTMLElement));
|
||||||
|
|
||||||
|
const ownerDocument: HTMLDocument = _target.ownerDocument;
|
||||||
|
const bodyElm = ownerDocument.body as HTMLBodyElement;
|
||||||
|
const wnd = ownerDocument.defaultView as Window;
|
||||||
|
const isTextareaHostGenerated = isTextarea && _host !== osTargetObj._host;
|
||||||
|
|
||||||
|
// only insert host for textarea after target if it was generated
|
||||||
|
if (isTextareaHostGenerated) {
|
||||||
|
insertAfter(_target, _host);
|
||||||
|
|
||||||
|
push(destroyFns, () => {
|
||||||
|
insertAfter(_host, _target);
|
||||||
|
removeElements(_host);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetIsElm) {
|
||||||
|
appendChildren(_content!, getTargetContents(_target));
|
||||||
|
appendChildren(_host, _padding);
|
||||||
|
|
||||||
|
push(destroyFns, () => {
|
||||||
|
appendChildren(_host, contents(_content));
|
||||||
|
removeElements(_padding);
|
||||||
|
removeClass(_host, classNameHost);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const contentContainingElm = _content || _viewport || _padding || _host;
|
||||||
|
const createPadding = isUndefined(_padding);
|
||||||
|
const createViewport = isUndefined(_viewport);
|
||||||
|
const createContent = isUndefined(_content);
|
||||||
|
const targetContents = getTargetContents(contentContainingElm);
|
||||||
|
|
||||||
|
_padding = osTargetObj._padding = createPadding ? createDiv() : _padding;
|
||||||
|
_viewport = osTargetObj._viewport = createViewport ? createDiv() : _viewport;
|
||||||
|
_content = osTargetObj._content = createContent ? createDiv() : _content;
|
||||||
|
|
||||||
|
appendChildren(_host, _padding);
|
||||||
|
appendChildren(_padding || _host, _viewport);
|
||||||
|
appendChildren(_viewport, _content);
|
||||||
|
|
||||||
|
const contentSlot = _content || _viewport;
|
||||||
|
appendChildren(contentSlot, targetContents);
|
||||||
|
|
||||||
|
push(destroyFns, () => {
|
||||||
|
if (createContent) {
|
||||||
|
unwrap(_content);
|
||||||
|
}
|
||||||
|
if (createViewport) {
|
||||||
|
unwrap(_viewport);
|
||||||
|
}
|
||||||
|
if (createPadding) {
|
||||||
|
unwrap(_padding);
|
||||||
|
}
|
||||||
|
removeClass(_host, classNameHost);
|
||||||
|
removeClass(_padding, classNamePadding);
|
||||||
|
removeClass(_viewport, classNameViewport);
|
||||||
|
removeClass(_content, classNameContent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addClass(_host, classNameHost);
|
||||||
|
addClass(_padding, classNamePadding);
|
||||||
|
addClass(_viewport, classNameViewport);
|
||||||
|
addClass(_content, classNameContent);
|
||||||
|
|
||||||
|
const ctx: OSTargetContext = {
|
||||||
|
_windowElm: wnd,
|
||||||
|
_documentElm: ownerDocument,
|
||||||
|
_htmlElm: parent(bodyElm) as HTMLHtmlElement,
|
||||||
|
_bodyElm: bodyElm,
|
||||||
|
_isTextarea: isTextarea,
|
||||||
|
_isBody: isBody,
|
||||||
|
};
|
||||||
|
// @ts-ignore
|
||||||
|
const obj: PreparedOSTargetObject = {
|
||||||
|
...osTargetObj,
|
||||||
|
_host,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
_targetObj: obj,
|
||||||
|
_targetCtx: ctx,
|
||||||
|
_destroy: () => {
|
||||||
|
runEach(destroyFns);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -3,7 +3,11 @@ import { each } from 'support/utils/array';
|
|||||||
import { keys } from 'support/utils/object';
|
import { keys } from 'support/utils/object';
|
||||||
|
|
||||||
const rnothtmlwhite = /[^\x20\t\r\n\f]+/g;
|
const rnothtmlwhite = /[^\x20\t\r\n\f]+/g;
|
||||||
const classListAction = (elm: Element | null, className: string, action: (elmClassList: DOMTokenList, clazz: string) => boolean | void): boolean => {
|
const classListAction = (
|
||||||
|
elm: Element | null | undefined,
|
||||||
|
className: string,
|
||||||
|
action: (elmClassList: DOMTokenList, clazz: string) => boolean | void
|
||||||
|
): boolean => {
|
||||||
let clazz: string;
|
let clazz: string;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let result = false;
|
let result = false;
|
||||||
@@ -23,7 +27,7 @@ const classListAction = (elm: Element | null, className: string, action: (elmCla
|
|||||||
* @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 | null, className: string): boolean =>
|
export const hasClass = (elm: Element | null | undefined, className: string): boolean =>
|
||||||
classListAction(elm, className, (classList, clazz) => classList.contains(clazz));
|
classListAction(elm, className, (classList, clazz) => classList.contains(clazz));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,7 +35,7 @@ export const hasClass = (elm: Element | null, className: string): boolean =>
|
|||||||
* @param elm The element.
|
* @param elm The element.
|
||||||
* @param className The class name(s) which shall be added. (separated by spaces)
|
* @param className The class name(s) which shall be added. (separated by spaces)
|
||||||
*/
|
*/
|
||||||
export const addClass = (elm: Element | null, className: string): void => {
|
export const addClass = (elm: Element | null | undefined, className: string): void => {
|
||||||
classListAction(elm, className, (classList, clazz) => classList.add(clazz));
|
classListAction(elm, className, (classList, clazz) => classList.add(clazz));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,7 +44,7 @@ export const addClass = (elm: Element | null, className: string): void => {
|
|||||||
* @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 | null, className: string): void => {
|
export const removeClass = (elm: Element | null | undefined, className: string): void => {
|
||||||
classListAction(elm, className, (classList, clazz) => classList.remove(clazz));
|
classListAction(elm, className, (classList, clazz) => classList.remove(clazz));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const windowSize = (): WH => ({
|
|||||||
* Returns the scroll- width and height of the passed element. If the element is null the width and height values are 0.
|
* Returns the scroll- width and height of the passed element. If the element is null the width and height values are 0.
|
||||||
* @param elm The element of which the scroll- width and height shall be returned.
|
* @param elm The element of which the scroll- width and height shall be returned.
|
||||||
*/
|
*/
|
||||||
export const offsetSize = (elm: HTMLElement | null): WH =>
|
export const offsetSize = (elm: HTMLElement | null | undefined): WH =>
|
||||||
elm
|
elm
|
||||||
? {
|
? {
|
||||||
w: elm.offsetWidth,
|
w: elm.offsetWidth,
|
||||||
@@ -33,7 +33,7 @@ export const offsetSize = (elm: HTMLElement | null): WH =>
|
|||||||
* Returns the client- width and height of the passed element. If the element is null the width and height values are 0.
|
* Returns the client- width and height of the passed element. If the element is null the width and height values are 0.
|
||||||
* @param elm The element of which the client- width and height shall be returned.
|
* @param elm The element of which the client- width and height shall be returned.
|
||||||
*/
|
*/
|
||||||
export const clientSize = (elm: HTMLElement | null): WH =>
|
export const clientSize = (elm: HTMLElement | null | undefined): WH =>
|
||||||
elm
|
elm
|
||||||
? {
|
? {
|
||||||
w: elm.clientWidth,
|
w: elm.clientWidth,
|
||||||
@@ -45,7 +45,7 @@ export const clientSize = (elm: HTMLElement | null): WH =>
|
|||||||
* Returns the client- width and height of the passed element. If the element is null the width and height values are 0.
|
* Returns the client- width and height of the passed element. If the element is null the width and height values are 0.
|
||||||
* @param elm The element of which the client- width and height shall be returned.
|
* @param elm The element of which the client- width and height shall be returned.
|
||||||
*/
|
*/
|
||||||
export const scrollSize = (elm: HTMLElement | null): WH =>
|
export const scrollSize = (elm: HTMLElement | null | undefined): WH =>
|
||||||
elm
|
elm
|
||||||
? {
|
? {
|
||||||
w: elm.scrollWidth,
|
w: elm.scrollWidth,
|
||||||
@@ -63,4 +63,4 @@ export const getBoundingClientRect = (elm: HTMLElement): DOMRect => elm.getBound
|
|||||||
* Determines whether the passed element has any dimensions.
|
* Determines whether the passed element has any dimensions.
|
||||||
* @param elm The element.
|
* @param elm The element.
|
||||||
*/
|
*/
|
||||||
export const hasDimensions = (elm: HTMLElement | null): boolean => (elm ? elementHasDimensions(elm as HTMLElement) : false);
|
export const hasDimensions = (elm: HTMLElement | null | undefined): boolean => (elm ? elementHasDimensions(elm as HTMLElement) : false);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { isArrayLike } from 'support/utils/types';
|
|||||||
import { each, from } from 'support/utils/array';
|
import { each, from } from 'support/utils/array';
|
||||||
import { parent } from 'support/dom/traversal';
|
import { parent } from 'support/dom/traversal';
|
||||||
|
|
||||||
type NodeCollection = ArrayLike<Node> | Node | undefined | null;
|
type NodeCollection = ArrayLike<Node> | Node | null | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts Nodes before the given preferredAnchor element.
|
* Inserts Nodes before the given preferredAnchor element.
|
||||||
@@ -10,10 +10,10 @@ type NodeCollection = ArrayLike<Node> | Node | undefined | null;
|
|||||||
* @param preferredAnchor The element before which the Nodes shall be inserted or null if the elements shall be appended at the end.
|
* @param preferredAnchor The element before which the Nodes shall be inserted or null if the elements shall be appended at the end.
|
||||||
* @param insertedElms The Nodes which shall be inserted.
|
* @param insertedElms The Nodes which shall be inserted.
|
||||||
*/
|
*/
|
||||||
const before = (parentElm: Node | null, preferredAnchor: Node | null, insertedElms: NodeCollection): void => {
|
const before = (parentElm: Node | null | undefined, preferredAnchor: Node | null | undefined, insertedElms: NodeCollection): void => {
|
||||||
if (insertedElms) {
|
if (insertedElms) {
|
||||||
let anchor: Node | null = preferredAnchor;
|
let anchor: Node | null | undefined = preferredAnchor;
|
||||||
let fragment: DocumentFragment | Node | undefined | null;
|
let fragment: DocumentFragment | Node | null | undefined;
|
||||||
|
|
||||||
// parent must be defined
|
// parent must be defined
|
||||||
if (parentElm) {
|
if (parentElm) {
|
||||||
@@ -40,7 +40,7 @@ const before = (parentElm: Node | null, preferredAnchor: Node | null, insertedEl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parentElm.insertBefore(fragment, anchor);
|
parentElm.insertBefore(fragment, anchor || null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -50,7 +50,7 @@ const before = (parentElm: Node | null, preferredAnchor: Node | null, insertedEl
|
|||||||
* @param node The Node to which the children shall be appended.
|
* @param node The Node to which the children shall be appended.
|
||||||
* @param children The Nodes which shall be appended.
|
* @param children The Nodes which shall be appended.
|
||||||
*/
|
*/
|
||||||
export const appendChildren = (node: Node | null, children: NodeCollection): void => {
|
export const appendChildren = (node: Node | null | undefined, children: NodeCollection): void => {
|
||||||
before(node, null, children);
|
before(node, null, children);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ export const appendChildren = (node: Node | null, children: NodeCollection): voi
|
|||||||
* @param node The Node to which the children shall be prepended.
|
* @param node The Node to which the children shall be prepended.
|
||||||
* @param children The Nodes which shall be prepended.
|
* @param children The Nodes which shall be prepended.
|
||||||
*/
|
*/
|
||||||
export const prependChildren = (node: Node | null, children: NodeCollection): void => {
|
export const prependChildren = (node: Node | null | undefined, children: NodeCollection): void => {
|
||||||
before(node, node && node.firstChild, children);
|
before(node, node && node.firstChild, children);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ export const prependChildren = (node: Node | null, children: NodeCollection): vo
|
|||||||
* @param node The Node before which the given Nodes shall be inserted.
|
* @param node The Node before which the given Nodes shall be inserted.
|
||||||
* @param insertedNodes The Nodes which shall be inserted.
|
* @param insertedNodes The Nodes which shall be inserted.
|
||||||
*/
|
*/
|
||||||
export const insertBefore = (node: Node | null, insertedNodes: NodeCollection): void => {
|
export const insertBefore = (node: Node | null | undefined, insertedNodes: NodeCollection): void => {
|
||||||
before(parent(node), node, insertedNodes);
|
before(parent(node), node, insertedNodes);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ export const insertBefore = (node: Node | null, insertedNodes: NodeCollection):
|
|||||||
* @param node The Node after which the given Nodes shall be inserted.
|
* @param node The Node after which the given Nodes shall be inserted.
|
||||||
* @param insertedNodes The Nodes which shall be inserted.
|
* @param insertedNodes The Nodes which shall be inserted.
|
||||||
*/
|
*/
|
||||||
export const insertAfter = (node: Node | null, insertedNodes: NodeCollection): void => {
|
export const insertAfter = (node: Node | null | undefined, insertedNodes: NodeCollection): void => {
|
||||||
before(parent(node), node && node.nextSibling, insertedNodes);
|
before(parent(node), node && node.nextSibling, insertedNodes);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const zeroObj: XY = {
|
|||||||
* Returns the offset- left and top coordinates of the passed element relative to the document. If the element is null the top and left values are 0.
|
* Returns the offset- left and top coordinates of the passed element relative to the document. If the element is null the top and left values are 0.
|
||||||
* @param elm The element of which the offset- top and left coordinates shall be returned.
|
* @param elm The element of which the offset- top and left coordinates shall be returned.
|
||||||
*/
|
*/
|
||||||
export const absoluteCoordinates = (elm: HTMLElement | null): XY => {
|
export const absoluteCoordinates = (elm: HTMLElement | null | undefined): XY => {
|
||||||
const rect = elm ? getBoundingClientRect(elm) : 0;
|
const rect = elm ? getBoundingClientRect(elm) : 0;
|
||||||
return rect
|
return rect
|
||||||
? {
|
? {
|
||||||
@@ -28,7 +28,7 @@ export const absoluteCoordinates = (elm: HTMLElement | null): XY => {
|
|||||||
* Returns the offset- left and top coordinates of the passed element. If the element is null the top and left values are 0.
|
* Returns the offset- left and top coordinates of the passed element. If the element is null the top and left values are 0.
|
||||||
* @param elm The element of which the offset- top and left coordinates shall be returned.
|
* @param elm The element of which the offset- top and left coordinates shall be returned.
|
||||||
*/
|
*/
|
||||||
export const offsetCoordinates = (elm: HTMLElement | null): XY =>
|
export const offsetCoordinates = (elm: HTMLElement | null | undefined): XY =>
|
||||||
elm
|
elm
|
||||||
? {
|
? {
|
||||||
x: elm.offsetLeft,
|
x: elm.offsetLeft,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const adaptCSSVal = (prop: string, val: string | number): string | number => (!c
|
|||||||
const getCSSVal = (elm: HTMLElement, computedStyle: CSSStyleDeclaration, prop: string): string =>
|
const getCSSVal = (elm: HTMLElement, computedStyle: CSSStyleDeclaration, prop: string): string =>
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
computedStyle != null ? computedStyle.getPropertyValue(prop) : elm.style[prop];
|
computedStyle != null ? computedStyle.getPropertyValue(prop) : elm.style[prop];
|
||||||
const setCSSVal = (elm: HTMLElement | null, prop: string, val: string | number): void => {
|
const setCSSVal = (elm: HTMLElement | null | undefined, prop: string, val: string | number): void => {
|
||||||
try {
|
try {
|
||||||
if (elm && elm.style[prop] !== undefined) {
|
if (elm && elm.style[prop] !== undefined) {
|
||||||
elm.style[prop] = adaptCSSVal(prop, val);
|
elm.style[prop] = adaptCSSVal(prop, val);
|
||||||
@@ -49,10 +49,10 @@ const setCSSVal = (elm: HTMLElement | null, prop: string, val: string | number):
|
|||||||
* @param elm The element to which the styles shall be applied to / be read from.
|
* @param elm The element to which the styles shall be applied to / be read from.
|
||||||
* @param styles The styles which shall be set or read.
|
* @param styles The styles which shall be set or read.
|
||||||
*/
|
*/
|
||||||
export function style(elm: HTMLElement | null, styles: CssStyles): void;
|
export function style(elm: HTMLElement | null | undefined, styles: CssStyles): void;
|
||||||
export function style(elm: HTMLElement | null, styles: string): string;
|
export function style(elm: HTMLElement | null | undefined, styles: string): string;
|
||||||
export function style(elm: HTMLElement | null, styles: Array<string> | string): { [key: string]: string };
|
export function style(elm: HTMLElement | null | undefined, styles: Array<string> | string): { [key: string]: string };
|
||||||
export function style(elm: HTMLElement | null, styles: CssStyles | Array<string> | string): { [key: string]: string } | string | void {
|
export function style(elm: HTMLElement | null | undefined, styles: CssStyles | Array<string> | string): { [key: string]: string } | string | void {
|
||||||
const getSingleStyle = isString(styles);
|
const getSingleStyle = isString(styles);
|
||||||
const getStyles = isArray(styles) || getSingleStyle;
|
const getStyles = isArray(styles) || getSingleStyle;
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ export const hide = (elm: HTMLElement | null): void => {
|
|||||||
* Shows the passed element (display: block).
|
* Shows the passed element (display: block).
|
||||||
* @param elm The element which shall be shown.
|
* @param elm The element which shall be shown.
|
||||||
*/
|
*/
|
||||||
export const show = (elm: HTMLElement | null): void => {
|
export const show = (elm: HTMLElement | null | undefined): void => {
|
||||||
style(elm, { display: 'block' });
|
style(elm, { display: 'block' });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ export const show = (elm: HTMLElement | null): void => {
|
|||||||
* @param elm
|
* @param elm
|
||||||
* @param property
|
* @param property
|
||||||
*/
|
*/
|
||||||
export const topRightBottomLeft = (elm: HTMLElement | null, property?: string): TRBL => {
|
export const topRightBottomLeft = (elm: HTMLElement | null | undefined, property?: string): TRBL => {
|
||||||
const finalProp = property || '';
|
const finalProp = property || '';
|
||||||
const top = `${finalProp}-top`;
|
const top = `${finalProp}-top`;
|
||||||
const right = `${finalProp}-right`;
|
const right = `${finalProp}-right`;
|
||||||
|
|||||||
@@ -4,15 +4,17 @@ export type OSTargetElement = HTMLElement | HTMLTextAreaElement;
|
|||||||
|
|
||||||
export interface OSTargetObject {
|
export interface OSTargetObject {
|
||||||
target: OSTargetElement;
|
target: OSTargetElement;
|
||||||
host: HTMLElement;
|
host?: HTMLElement;
|
||||||
padding: HTMLElement;
|
padding?: HTMLElement | null;
|
||||||
viewport: HTMLElement;
|
viewport?: HTMLElement;
|
||||||
content: HTMLElement;
|
content?: HTMLElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OSTarget = OSTargetElement | OSTargetObject;
|
export type InternalVersionOf<T> = {
|
||||||
|
[K in keyof T as `_${Uncapitalize<string & K>}`]: T[K];
|
||||||
|
};
|
||||||
|
|
||||||
export type CSSDirection = 'ltr' | 'rtl';
|
export type OSTarget = OSTargetElement | OSTargetObject;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
export namespace OverlayScrollbars {
|
export namespace OverlayScrollbars {
|
||||||
|
|||||||
@@ -0,0 +1,449 @@
|
|||||||
|
import { createStructureSetup, StructureSetup } from 'setups/structureSetup';
|
||||||
|
|
||||||
|
const textareaId = 'textarea';
|
||||||
|
const textareaHostId = 'host';
|
||||||
|
const elementId = 'target';
|
||||||
|
const dynamicContent = 'text<p>paragraph</p>';
|
||||||
|
const textareaContent = `<textarea id="${textareaId}">text</textarea>`;
|
||||||
|
const getSnapshot = () => document.body.innerHTML;
|
||||||
|
const getTarget = (textarea?: boolean) => document.getElementById(textarea ? textareaId : elementId)!;
|
||||||
|
const fillBody = (textarea?: boolean, customDOM?: (content: string, hostId: string) => string) => {
|
||||||
|
document.body.innerHTML = `
|
||||||
|
<nav></nav>
|
||||||
|
${
|
||||||
|
customDOM
|
||||||
|
? customDOM(textarea ? textareaContent : dynamicContent, textarea ? textareaHostId : elementId)
|
||||||
|
: textarea
|
||||||
|
? textareaContent
|
||||||
|
: `<div id="${elementId}">${dynamicContent}</div>`
|
||||||
|
}
|
||||||
|
<footer></footer>
|
||||||
|
`;
|
||||||
|
return getSnapshot();
|
||||||
|
};
|
||||||
|
const clearBody = () => {
|
||||||
|
document.body.innerHTML = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getElements = (textarea?: boolean) => {
|
||||||
|
const target = getTarget(textarea);
|
||||||
|
const host = document.querySelector('.os-host')!;
|
||||||
|
const padding = document.querySelector('.os-padding')!;
|
||||||
|
const viewport = document.querySelector('.os-viewport')!;
|
||||||
|
const content = document.querySelector('.os-content')!;
|
||||||
|
|
||||||
|
return {
|
||||||
|
target,
|
||||||
|
host,
|
||||||
|
padding,
|
||||||
|
viewport,
|
||||||
|
content,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertCorrectDOMStructure = (textarea?: boolean) => {
|
||||||
|
const { target, host, padding, viewport, content } = getElements(textarea);
|
||||||
|
|
||||||
|
expect(host).toBeTruthy();
|
||||||
|
expect(viewport).toBeTruthy();
|
||||||
|
expect(viewport.parentElement).toBe(padding || host);
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
expect(content.parentElement).toBe(viewport);
|
||||||
|
}
|
||||||
|
if (padding) {
|
||||||
|
expect(padding.parentElement).toBe(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(host.parentElement).toBe(document.body);
|
||||||
|
expect(host.previousElementSibling).toBe(document.querySelector('nav'));
|
||||||
|
expect(host.nextElementSibling).toBe(document.querySelector('footer'));
|
||||||
|
|
||||||
|
const contentElm = content || viewport;
|
||||||
|
if (textarea) {
|
||||||
|
expect(target.parentElement).toBe(contentElm);
|
||||||
|
expect(contentElm.innerHTML).toBe(textareaContent);
|
||||||
|
} else {
|
||||||
|
expect(target).toBe(host);
|
||||||
|
expect(contentElm.innerHTML).toBe(dynamicContent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertCorrectSetup = (textarea: boolean, setup: StructureSetup) => {
|
||||||
|
const { _targetObj, _targetCtx, _destroy } = setup;
|
||||||
|
const { _target, _host, _padding, _viewport, _content } = _targetObj;
|
||||||
|
const { target, host, padding, viewport, content } = getElements(textarea);
|
||||||
|
const isTextarea = target.matches('textarea');
|
||||||
|
const isBody = target.matches('body');
|
||||||
|
|
||||||
|
expect(textarea).toBe(isTextarea);
|
||||||
|
|
||||||
|
expect(_target).toBe(target);
|
||||||
|
expect(_host).toBe(host);
|
||||||
|
|
||||||
|
if (padding || _padding) {
|
||||||
|
expect(_padding).toBe(padding);
|
||||||
|
} else {
|
||||||
|
expect(padding).toBeFalsy();
|
||||||
|
expect(_padding).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewport || _viewport) {
|
||||||
|
expect(_viewport).toBe(viewport);
|
||||||
|
} else {
|
||||||
|
expect(viewport).toBeFalsy();
|
||||||
|
expect(_viewport).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content || _content) {
|
||||||
|
expect(_content).toBe(content);
|
||||||
|
} else {
|
||||||
|
expect(content).toBeFalsy();
|
||||||
|
expect(_content).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { _isTextarea, _isBody, _bodyElm, _htmlElm, _documentElm, _windowElm } = _targetCtx;
|
||||||
|
|
||||||
|
expect(_isTextarea).toBe(isTextarea);
|
||||||
|
expect(_isBody).toBe(isBody);
|
||||||
|
expect(_windowElm).toBe(document.defaultView);
|
||||||
|
expect(_documentElm).toBe(document);
|
||||||
|
expect(_htmlElm).toBe(document.body.parentElement);
|
||||||
|
expect(_bodyElm).toBe(document.body);
|
||||||
|
|
||||||
|
expect(typeof _destroy).toBe('function');
|
||||||
|
|
||||||
|
return setup;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertCorrectDestroy = (snapshot: string, setup: StructureSetup) => {
|
||||||
|
const { _destroy } = setup;
|
||||||
|
|
||||||
|
_destroy();
|
||||||
|
|
||||||
|
// remove empty class attr
|
||||||
|
const elms = document.querySelectorAll('*');
|
||||||
|
Array.from(elms).forEach((elm) => {
|
||||||
|
const classAttr = elm.getAttribute('class');
|
||||||
|
if (classAttr === '') {
|
||||||
|
elm.removeAttribute('class');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(snapshot).toBe(getSnapshot());
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('structureSetup', () => {
|
||||||
|
afterEach(() => clearBody());
|
||||||
|
|
||||||
|
[false, true].forEach((isTextarea) => {
|
||||||
|
describe(isTextarea ? 'textarea' : 'element', () => {
|
||||||
|
describe('basic', () => {
|
||||||
|
test('Element', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(isTextarea, createStructureSetup(getTarget(isTextarea)));
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Object', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(isTextarea, createStructureSetup({ target: getTarget(isTextarea) }));
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('complex', () => {
|
||||||
|
describe('single assigned', () => {
|
||||||
|
test('padding', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('multiple assigned', () => {
|
||||||
|
test('padding viewport content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding"><div id="viewport"><div id="content">${content}</div></div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('padding viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('padding content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding"><div id="content">${content}</div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('viewport content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('single null', () => {
|
||||||
|
test('padding', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
content: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('multiple null', () => {
|
||||||
|
test('padding & content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea);
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: null,
|
||||||
|
content: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('mixed', () => {
|
||||||
|
test('null: padding & content | assigned: viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: null,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('null: padding | assigned: content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: null,
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('null: padding | assigned: viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: null,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('null: padding | assigned: viewport & content', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
padding: null,
|
||||||
|
content: document.querySelector<HTMLElement>('#content')!,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('null: content | assigned: padding', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
content: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('null: content | assigned: viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('null: content | assigned: padding & viewport', () => {
|
||||||
|
const snapshot = fillBody(isTextarea, (content, hostId) => {
|
||||||
|
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
|
||||||
|
});
|
||||||
|
const setup = assertCorrectSetup(
|
||||||
|
isTextarea,
|
||||||
|
createStructureSetup({
|
||||||
|
host: document.querySelector<HTMLElement>('#host')!,
|
||||||
|
target: getTarget(isTextarea),
|
||||||
|
padding: document.querySelector<HTMLElement>('#padding')!,
|
||||||
|
viewport: document.querySelector<HTMLElement>('#viewport')!,
|
||||||
|
content: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assertCorrectDOMStructure(isTextarea);
|
||||||
|
assertCorrectDestroy(snapshot, setup);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user