mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-05-24 15:24:08 +03:00
Create LifecyclesBase function
This commit is contained in:
@@ -9,21 +9,17 @@ import {
|
||||
topRightBottomLeft,
|
||||
TRBL,
|
||||
equalTRBL,
|
||||
createCache,
|
||||
optionsTemplateTypes as oTypes,
|
||||
transformOptions,
|
||||
validateOptions,
|
||||
OptionsTemplateValue,
|
||||
assignDeep,
|
||||
} from 'support';
|
||||
import { Lifecycle } from 'overlayscrollbars/lifecycles';
|
||||
import { createLifecycleBase, Lifecycle } from 'overlayscrollbars/lifecycles';
|
||||
import { getEnvironment, Environment } from 'environment';
|
||||
import { createSizeObserver } from 'overlayscrollbars/observers/SizeObserver';
|
||||
import { createTrinsicObserver } from 'overlayscrollbars/observers/TrinsicObserver';
|
||||
|
||||
export type OverflowBehavior = 'hidden' | 'scroll' | 'visible-hidden' | 'visible-scroll';
|
||||
export interface StructureLifecycleOptions {
|
||||
paddingAbsolute?: boolean;
|
||||
paddingAbsolute: boolean;
|
||||
overflowBehavior?: {
|
||||
x?: OverflowBehavior;
|
||||
y?: OverflowBehavior;
|
||||
@@ -34,13 +30,6 @@ interface StructureLifecycleCache {
|
||||
}
|
||||
|
||||
const overflowBehaviorAllowedValues: OptionsTemplateValue<OverflowBehavior> = 'visible-hidden visible-scroll scroll hidden';
|
||||
const { _template: optionsTemplate, _options: defaultOptions } = transformOptions<Required<StructureLifecycleOptions>>({
|
||||
paddingAbsolute: [false, oTypes.boolean],
|
||||
overflowBehavior: {
|
||||
x: ['scroll', overflowBehaviorAllowedValues],
|
||||
y: ['scroll', overflowBehaviorAllowedValues],
|
||||
},
|
||||
});
|
||||
|
||||
const classNameHost = 'os-host';
|
||||
const classNameViewport = 'os-viewport';
|
||||
@@ -51,12 +40,6 @@ const cssMarginEnd = cssProperty('margin-inline-end');
|
||||
const cssBorderEnd = cssProperty('border-inline-end');
|
||||
|
||||
export const createStructureLifecycle = (target: HTMLElement, initialOptions?: StructureLifecycleOptions): Lifecycle<StructureLifecycleOptions> => {
|
||||
const options: Required<StructureLifecycleOptions> = assignDeep(
|
||||
{},
|
||||
defaultOptions,
|
||||
validateOptions<StructureLifecycleOptions>(initialOptions || {}, optionsTemplate)._validated
|
||||
);
|
||||
|
||||
const destructFns: (() => any)[] = [];
|
||||
const env: Environment = getEnvironment();
|
||||
const scrollbarsOverlaid = env._nativeScrollbarIsOverlaid;
|
||||
@@ -68,15 +51,30 @@ export const createStructureLifecycle = (target: HTMLElement, initialOptions?: S
|
||||
const viewportElm = createDOM(`<div class="${classNameViewport} ${classNameViewportScrollbarStyling}"></div>`)[0];
|
||||
const contentElm = createDOM(`<div class="${classNameContent}"></div>`)[0];
|
||||
|
||||
const updateCache = createCache<StructureLifecycleCache>({
|
||||
padding: [() => topRightBottomLeft(target, 'padding'), equalTRBL],
|
||||
});
|
||||
const { _options, _update, _cacheChange } = createLifecycleBase<StructureLifecycleOptions, StructureLifecycleCache>(
|
||||
{
|
||||
paddingAbsolute: [false, oTypes.boolean],
|
||||
overflowBehavior: {
|
||||
x: ['scroll', overflowBehaviorAllowedValues],
|
||||
y: ['scroll', overflowBehaviorAllowedValues],
|
||||
},
|
||||
},
|
||||
{
|
||||
padding: [() => topRightBottomLeft(target, 'padding'), equalTRBL],
|
||||
},
|
||||
initialOptions,
|
||||
(changedOptions, changedCache) => {
|
||||
console.log(changedOptions); // eslint-disable-line
|
||||
console.log(changedCache); // eslint-disable-line
|
||||
}
|
||||
);
|
||||
|
||||
// eslint-disable-next-line
|
||||
const onSizeChanged = (direction?: 'ltr' | 'rtl') => {
|
||||
updateCache('padding');
|
||||
_cacheChange('padding');
|
||||
};
|
||||
const onTrinsicChanged = (widthIntrinsic: boolean, heightIntrinsic: boolean) => {
|
||||
console.log('heightAuot', heightIntrinsic);
|
||||
console.log('heightAuot', heightIntrinsic); // eslint-disable-line
|
||||
};
|
||||
|
||||
appendChildren(viewportElm, contentElm);
|
||||
@@ -88,14 +86,8 @@ export const createStructureLifecycle = (target: HTMLElement, initialOptions?: S
|
||||
destructFns.push(createTrinsicObserver(target, onTrinsicChanged));
|
||||
|
||||
return {
|
||||
_options(newOptions?: StructureLifecycleOptions) {
|
||||
// eslint-disable-next-line
|
||||
console.log('_options', newOptions);
|
||||
},
|
||||
_update(force?: boolean) {
|
||||
// eslint-disable-next-line
|
||||
console.log('_options', force);
|
||||
},
|
||||
_options,
|
||||
_update,
|
||||
_destruct() {
|
||||
runEach(destructFns);
|
||||
removeElements(viewportElm);
|
||||
|
||||
@@ -1,7 +1,75 @@
|
||||
import {
|
||||
CacheUpdateInfo,
|
||||
CachePropsToUpdate,
|
||||
CacheChanged,
|
||||
OptionsWithOptionsTemplate,
|
||||
OptionsValidated,
|
||||
transformOptions,
|
||||
validateOptions,
|
||||
assignDeep,
|
||||
createCache,
|
||||
} from 'support';
|
||||
import { PlainObject } from 'typings';
|
||||
|
||||
export interface Lifecycle<T = PlainObject> {
|
||||
_options(options?: T): void;
|
||||
interface LifecycleUpdateHints<O, C> {
|
||||
_force?: boolean;
|
||||
_changedOptions?: OptionsValidated<O>;
|
||||
_changedCache?: CachePropsToUpdate<C>;
|
||||
}
|
||||
|
||||
interface AbstractLifecycle<O extends PlainObject> {
|
||||
_options(newOptions?: O): O;
|
||||
_update(force?: boolean): void;
|
||||
}
|
||||
|
||||
export interface Lifecycle<T extends PlainObject> extends AbstractLifecycle<T> {
|
||||
_destruct(): void;
|
||||
}
|
||||
|
||||
export interface LifecycleBase<O extends PlainObject, C extends PlainObject> extends AbstractLifecycle<O> {
|
||||
_cacheChange(cachePropsToUpdate?: CachePropsToUpdate<C>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a object which can be seen as the base of a lifecycle because it provides all the tools to manage a lifecycle and its options, cache and base functions.
|
||||
* @param defaultOptionsWithTemplate A object which describes the options and the default options of the lifecycle.
|
||||
* @param cacheUpdateInfo A object which describes how cache updates shall behave.
|
||||
* @param initialOptions The initialOptions for the lifecylce. (Can be undefined)
|
||||
* @param updateFunction The update function where cache and options updates are handled. Has two arguments which are the changedOptions and the changedCache objects.
|
||||
*/
|
||||
export const createLifecycleBase = <O, C>(
|
||||
defaultOptionsWithTemplate: OptionsWithOptionsTemplate<Required<O>>,
|
||||
cacheUpdateInfo: CacheUpdateInfo<C>,
|
||||
initialOptions: O | undefined,
|
||||
updateFunction: (changedOptions: OptionsValidated<O>, changedCache: CacheChanged<C>) => any
|
||||
): LifecycleBase<O, C> => {
|
||||
const { _template: optionsTemplate, _options: defaultOptions } = transformOptions<Required<O>>(defaultOptionsWithTemplate);
|
||||
const options: Required<O> = assignDeep({}, defaultOptions, validateOptions<O>(initialOptions || ({} as O), optionsTemplate)._validated);
|
||||
const cacheChange = createCache<C>(cacheUpdateInfo);
|
||||
|
||||
const update = (hints: LifecycleUpdateHints<O, C>) => {
|
||||
const force = hints._force === true;
|
||||
const changedCache = cacheChange(force ? undefined : hints._changedCache, force);
|
||||
const changedOptions = force ? ({} as O) : hints._changedOptions || ({} as O);
|
||||
|
||||
updateFunction(changedOptions, changedCache);
|
||||
};
|
||||
|
||||
return {
|
||||
_options(newOptions?: O) {
|
||||
if (newOptions) {
|
||||
const { _validated: changedOptions } = validateOptions(newOptions, optionsTemplate, options, true);
|
||||
assignDeep(options, changedOptions);
|
||||
|
||||
update({ _changedOptions: changedOptions });
|
||||
}
|
||||
return options;
|
||||
},
|
||||
_update: (force?: boolean) => {
|
||||
update({ _force: force });
|
||||
},
|
||||
_cacheChange: (cachePropsToUpdate?: CachePropsToUpdate<C>) => {
|
||||
update({ _changedCache: cachePropsToUpdate });
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
+5
-3
@@ -12,12 +12,14 @@ type Cache<T> = {
|
||||
|
||||
type UpdateCacheProp<T> = <P extends keyof T>(prop: P, value: T[P], compare: CacheEqualFunction<T, P> | null) => void;
|
||||
|
||||
type PropsToUpdate<T> = Array<keyof T> | keyof T;
|
||||
export type CachePropsToUpdate<T> = Array<keyof T> | keyof T;
|
||||
|
||||
export type CacheUpdateFunction<T, P extends keyof T> = (current?: T[P], previous?: T[P]) => T[P];
|
||||
|
||||
export type CacheEqualFunction<T, P extends keyof T> = (a?: T[P], b?: T[P]) => boolean;
|
||||
|
||||
export type CacheChange<T> = (propsToUpdate?: CachePropsToUpdate<T>, force?: boolean) => CacheChanged<T>;
|
||||
|
||||
export type CacheChanged<T> = {
|
||||
[P in keyof T]?: T[P];
|
||||
};
|
||||
@@ -44,7 +46,7 @@ export type CacheUpdateInfo<T> = {
|
||||
* @returns A function which can be called with wither one ar an array of properties which shall be updated. Optionally it can be called with the force param.
|
||||
* This function returns a object which contains all changed cache properties, if a property isn't in this object it means that it didn't change.
|
||||
*/
|
||||
export const createCache = <T>(cacheUpdateInfo: CacheUpdateInfo<T>): ((propsToUpdate?: PropsToUpdate<T>, force?: boolean) => CacheChanged<T>) => {
|
||||
export const createCache = <T>(cacheUpdateInfo: CacheUpdateInfo<T>): CacheChange<T> => {
|
||||
const cache: Cache<T> = {} as T;
|
||||
const allProps: Array<keyof T> = keys(cacheUpdateInfo) as Array<keyof T>;
|
||||
|
||||
@@ -73,7 +75,7 @@ export const createCache = <T>(cacheUpdateInfo: CacheUpdateInfo<T>): ((propsToUp
|
||||
return result;
|
||||
};
|
||||
|
||||
return (propsToUpdate?: PropsToUpdate<T>, force?: boolean) => {
|
||||
return (propsToUpdate?: CachePropsToUpdate<T>, force?: boolean) => {
|
||||
const finalPropsToUpdate: Array<keyof T> =
|
||||
(isString(propsToUpdate) ? ([propsToUpdate] as Array<keyof T>) : (propsToUpdate as Array<keyof T>)) || allProps;
|
||||
each(finalPropsToUpdate, (prop) => {
|
||||
|
||||
@@ -19,9 +19,12 @@ export type OptionsTemplate<T extends Required<T>> = {
|
||||
? OptionsTemplateValue<T[P]>
|
||||
: never;
|
||||
};
|
||||
export type OptionsValidatedResult<T> = {
|
||||
export type OptionsValidated<T> = {
|
||||
[P in keyof T]?: T[P];
|
||||
};
|
||||
export type OptionsValidationResult<T> = {
|
||||
readonly _foreign: PlainObject;
|
||||
readonly _validated: T;
|
||||
readonly _validated: OptionsValidated<T>;
|
||||
};
|
||||
// Options With Options Template Typings:
|
||||
export type OptionsWithOptionsTemplateValue<T extends OptionsTemplateNativeTypes> = [T, OptionsTemplateValue<T>];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { each, indexOf, hasOwnProperty, keys } from 'support/utils';
|
||||
import { type, isArray, isUndefined, isEmptyObject, isPlainObject, isString } from 'support/utils/types';
|
||||
import { OptionsTemplate, OptionsTemplateTypes, OptionsTemplateType, Func, OptionsValidatedResult } from 'support/options';
|
||||
import { OptionsTemplate, OptionsTemplateTypes, OptionsTemplateType, Func, OptionsValidationResult, OptionsValidated } from 'support/options';
|
||||
import { PlainObject } from 'typings';
|
||||
|
||||
const { stringify } = JSON;
|
||||
@@ -46,8 +46,8 @@ const validateRecursive = <T extends PlainObject>(
|
||||
optionsDiff: T,
|
||||
doWriteErrors?: boolean,
|
||||
propPath?: string
|
||||
): OptionsValidatedResult<T> => {
|
||||
const validatedOptions: T = {} as T;
|
||||
): OptionsValidationResult<T> => {
|
||||
const validatedOptions: OptionsValidated<T> = {};
|
||||
const optionsCopy: T = { ...options };
|
||||
const props = keys(template).filter((prop) => hasOwnProperty(options, prop));
|
||||
|
||||
@@ -61,7 +61,7 @@ const validateRecursive = <T extends PlainObject>(
|
||||
// if the template has a object as value, it means that the options are complex (verschachtelt)
|
||||
if (templateIsComplex && isPlainObject(optionsValue)) {
|
||||
const validatedResult = validateRecursive(optionsValue, templateValue as PlainObject, optionsDiffValue, doWriteErrors, propPrefix + prop);
|
||||
validatedOptions[prop] = validatedResult._validated;
|
||||
validatedOptions[prop] = validatedResult._validated as any;
|
||||
optionsCopy[prop] = validatedResult._foreign as any;
|
||||
|
||||
each([optionsCopy, validatedOptions], (value) => {
|
||||
@@ -145,7 +145,7 @@ const validateOptions = <T extends PlainObject>(
|
||||
template: OptionsTemplate<Required<T>>,
|
||||
optionsDiff?: T,
|
||||
doWriteErrors?: boolean
|
||||
): OptionsValidatedResult<T> => {
|
||||
): OptionsValidationResult<T> => {
|
||||
/*
|
||||
if (!isEmptyObject(foreign) && doWriteErrors)
|
||||
console.warn(`The following options are discarded due to invalidity:\r\n ${window.JSON.stringify(foreign, null, 2)}`);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { OptionsTemplate, OptionsTemplateType, Func, OptionsValidatedResult } from 'support/options';
|
||||
import { OptionsTemplate, OptionsTemplateType, Func, OptionsValidationResult } from 'support/options';
|
||||
import { PlainObject } from 'typings';
|
||||
declare const optionsTemplateTypes: OptionsTemplateTypesDictionary;
|
||||
declare const validate: <T extends PlainObject<any>>(options: T, template: OptionsTemplate<Required<T>>, optionsDiff?: T | undefined, doWriteErrors?: boolean | undefined) => OptionsValidatedResult<T>;
|
||||
declare const validate: <T extends PlainObject<any>>(options: T, template: OptionsTemplate<Required<T>>, optionsDiff?: T | undefined, doWriteErrors?: boolean | undefined) => OptionsValidationResult<T>;
|
||||
export { validate, optionsTemplateTypes };
|
||||
declare type OptionsTemplateTypesDictionary = {
|
||||
readonly boolean: OptionsTemplateType<boolean>;
|
||||
|
||||
Reference in New Issue
Block a user