new setup and update structure

This commit is contained in:
Rene
2022-06-28 02:07:45 +02:00
parent 43f86b74c8
commit 88eebe95ed
32 changed files with 2380 additions and 2224 deletions
@@ -0,0 +1,239 @@
import {
isHTMLElement,
appendChildren,
is,
createDiv,
contents,
insertAfter,
addClass,
parent,
indexOf,
removeElements,
removeClass,
push,
runEach,
insertBefore,
attr,
isBoolean,
isFunction,
keys,
} from 'support';
import {
classNameHost,
classNamePadding,
classNameViewport,
classNameViewportArrange,
classNameContent,
classNameViewportScrollbarStyling,
} from 'classnames';
import {
getEnvironment,
StructureInitializationStaticElement,
StructureInitializationDynamicElement,
StructureInitializationStrategy,
} from 'environment';
import { OSTarget, OSTargetElement, StructureInitialization } from 'typings';
export type StructureSetupElements = [targetObj: StructureSetupElementsObj, destroy: () => void];
export interface StructureSetupElementsObj {
_target: OSTargetElement;
_host: HTMLElement;
_viewport: HTMLElement;
_padding: HTMLElement | false;
_content: HTMLElement | false;
_viewportArrange: HTMLStyleElement | false;
// ctx ----
_isTextarea: boolean;
_isBody: boolean;
_htmlElm: HTMLHtmlElement;
_bodyElm: HTMLBodyElement;
_windowElm: Window;
_documentElm: Document;
_targetIsElm: boolean;
}
let contentArrangeCounter = 0;
const unwrap = (elm: HTMLElement | false | null | undefined) => {
appendChildren(parent(elm), contents(elm));
removeElements(elm);
};
const createUniqueViewportArrangeElement = (): HTMLStyleElement | false => {
const { _nativeScrollbarStyling, _nativeScrollbarIsOverlaid, _cssCustomProperties } =
getEnvironment();
/* istanbul ignore next */
const create =
!_cssCustomProperties &&
!_nativeScrollbarStyling &&
(_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y);
const result = create ? document.createElement('style') : false;
if (result) {
attr(result, 'id', `${classNameViewportArrange}-${contentArrangeCounter}`);
contentArrangeCounter++;
}
return result;
};
const staticCreationFromStrategy = (
target: OSTargetElement,
initializationValue: HTMLElement | undefined,
strategy: StructureInitializationStaticElement,
elementClass: string
): HTMLElement => {
const result =
initializationValue || (isFunction(strategy) ? strategy(target) : (strategy as null));
return result || createDiv(elementClass);
};
const dynamicCreationFromStrategy = (
target: OSTargetElement,
initializationValue: HTMLElement | boolean | undefined,
strategy: StructureInitializationDynamicElement,
elementClass: string,
defaultValue: boolean
): HTMLElement | false => {
const takeInitializationValue = isBoolean(initializationValue) || initializationValue;
const result = takeInitializationValue
? (initializationValue as boolean | HTMLElement)
: isFunction(strategy)
? strategy(target)
: strategy;
if (result === null) {
return defaultValue ? createDiv(elementClass) : false;
}
return result === true ? createDiv(elementClass) : result;
};
export const createStructureSetupElements = (target: OSTarget): StructureSetupElements => {
const { _getInitializationStrategy, _nativeScrollbarStyling } = getEnvironment();
const {
_host: hostInitializationStrategy,
_viewport: viewportInitializationStrategy,
_padding: paddingInitializationStrategy,
_content: contentInitializationStrategy,
} = _getInitializationStrategy() as StructureInitializationStrategy;
const targetIsElm = isHTMLElement(target);
const targetStructureInitialization = target as StructureInitialization;
const targetElement = targetIsElm
? (target as OSTargetElement)
: targetStructureInitialization.target;
const isTextarea = is(targetElement, 'textarea');
const isBody = !isTextarea && is(targetElement, 'body');
const ownerDocument: HTMLDocument = targetElement!.ownerDocument;
const bodyElm = ownerDocument.body as HTMLBodyElement;
const wnd = ownerDocument.defaultView as Window;
const evaluatedTargetObj: StructureSetupElementsObj = {
_target: targetElement,
_host: isTextarea
? staticCreationFromStrategy(
targetElement,
targetStructureInitialization.host,
hostInitializationStrategy,
classNameHost
)
: (targetElement as HTMLElement),
_viewport: staticCreationFromStrategy(
targetElement,
targetStructureInitialization.viewport,
viewportInitializationStrategy,
classNameViewport
),
_padding: dynamicCreationFromStrategy(
targetElement,
targetStructureInitialization.padding,
paddingInitializationStrategy,
classNamePadding,
!_nativeScrollbarStyling // default value for padding
),
_content: dynamicCreationFromStrategy(
targetElement,
targetStructureInitialization.content,
contentInitializationStrategy,
classNameContent,
false // default value for content
),
_viewportArrange: createUniqueViewportArrangeElement(),
_windowElm: wnd,
_documentElm: ownerDocument,
_htmlElm: parent(bodyElm) as HTMLHtmlElement,
_bodyElm: bodyElm,
_isTextarea: isTextarea,
_isBody: isBody,
_targetIsElm: targetIsElm,
};
const generatedElements = keys(evaluatedTargetObj).reduce((arr, key: string) => {
const value = evaluatedTargetObj[key];
return push(arr, value && !parent(value) ? value : false);
}, [] as HTMLElement[]);
const elementIsGenerated = (elm: HTMLElement | false) =>
elm ? indexOf(generatedElements, elm) > -1 : null;
const { _target, _host, _padding, _viewport, _content, _viewportArrange } = evaluatedTargetObj;
const destroyFns: (() => any)[] = [];
const isTextareaHostGenerated = isTextarea && elementIsGenerated(_host);
const targetContents = isTextarea
? _target
: contents(
[_content, _viewport, _padding, _host, _target].find(
(elm) => elementIsGenerated(elm) === false
)
);
const contentSlot = _content || _viewport;
// only insert host for textarea after target if it was generated
if (isTextareaHostGenerated) {
insertAfter(_target, _host);
push(destroyFns, () => {
insertAfter(_host, _target);
removeElements(_host);
});
}
appendChildren(contentSlot, targetContents);
appendChildren(_host, _padding);
appendChildren(_padding || _host, _viewport);
appendChildren(_viewport, _content);
addClass(_host, classNameHost);
addClass(_padding, classNamePadding);
addClass(_viewport, classNameViewport);
addClass(_content, classNameContent);
push(destroyFns, () => {
if (targetIsElm) {
appendChildren(_host, contents(contentSlot));
removeElements(_padding || _viewport);
removeClass(_host, classNameHost);
} else {
if (elementIsGenerated(_content)) {
unwrap(_content);
}
if (elementIsGenerated(_viewport)) {
unwrap(_viewport);
}
if (elementIsGenerated(_padding)) {
unwrap(_padding);
}
removeClass(_host, classNameHost);
removeClass(_padding, classNamePadding);
removeClass(_viewport, classNameViewport);
removeClass(_content, classNameContent);
}
});
if (_nativeScrollbarStyling) {
push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling));
}
if (_viewportArrange) {
insertBefore(_viewport, _viewportArrange);
push(destroyFns, removeElements.bind(0, _viewportArrange));
}
return [evaluatedTargetObj, runEach.bind(0, destroyFns)];
};