mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-20 02:00:37 +03:00
Create LifecyclesBase function
This commit is contained in:
@@ -9,21 +9,17 @@ import {
|
|||||||
topRightBottomLeft,
|
topRightBottomLeft,
|
||||||
TRBL,
|
TRBL,
|
||||||
equalTRBL,
|
equalTRBL,
|
||||||
createCache,
|
|
||||||
optionsTemplateTypes as oTypes,
|
optionsTemplateTypes as oTypes,
|
||||||
transformOptions,
|
|
||||||
validateOptions,
|
|
||||||
OptionsTemplateValue,
|
OptionsTemplateValue,
|
||||||
assignDeep,
|
|
||||||
} from 'support';
|
} from 'support';
|
||||||
import { Lifecycle } from 'overlayscrollbars/lifecycles';
|
import { createLifecycleBase, Lifecycle } from 'overlayscrollbars/lifecycles';
|
||||||
import { getEnvironment, Environment } from 'environment';
|
import { getEnvironment, Environment } from 'environment';
|
||||||
import { createSizeObserver } from 'overlayscrollbars/observers/SizeObserver';
|
import { createSizeObserver } from 'overlayscrollbars/observers/SizeObserver';
|
||||||
import { createTrinsicObserver } from 'overlayscrollbars/observers/TrinsicObserver';
|
import { createTrinsicObserver } from 'overlayscrollbars/observers/TrinsicObserver';
|
||||||
|
|
||||||
export type OverflowBehavior = 'hidden' | 'scroll' | 'visible-hidden' | 'visible-scroll';
|
export type OverflowBehavior = 'hidden' | 'scroll' | 'visible-hidden' | 'visible-scroll';
|
||||||
export interface StructureLifecycleOptions {
|
export interface StructureLifecycleOptions {
|
||||||
paddingAbsolute?: boolean;
|
paddingAbsolute: boolean;
|
||||||
overflowBehavior?: {
|
overflowBehavior?: {
|
||||||
x?: OverflowBehavior;
|
x?: OverflowBehavior;
|
||||||
y?: OverflowBehavior;
|
y?: OverflowBehavior;
|
||||||
@@ -34,13 +30,6 @@ interface StructureLifecycleCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const overflowBehaviorAllowedValues: OptionsTemplateValue<OverflowBehavior> = 'visible-hidden visible-scroll scroll hidden';
|
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 classNameHost = 'os-host';
|
||||||
const classNameViewport = 'os-viewport';
|
const classNameViewport = 'os-viewport';
|
||||||
@@ -51,12 +40,6 @@ const cssMarginEnd = cssProperty('margin-inline-end');
|
|||||||
const cssBorderEnd = cssProperty('border-inline-end');
|
const cssBorderEnd = cssProperty('border-inline-end');
|
||||||
|
|
||||||
export const createStructureLifecycle = (target: HTMLElement, initialOptions?: StructureLifecycleOptions): Lifecycle<StructureLifecycleOptions> => {
|
export const createStructureLifecycle = (target: HTMLElement, initialOptions?: StructureLifecycleOptions): Lifecycle<StructureLifecycleOptions> => {
|
||||||
const options: Required<StructureLifecycleOptions> = assignDeep(
|
|
||||||
{},
|
|
||||||
defaultOptions,
|
|
||||||
validateOptions<StructureLifecycleOptions>(initialOptions || {}, optionsTemplate)._validated
|
|
||||||
);
|
|
||||||
|
|
||||||
const destructFns: (() => any)[] = [];
|
const destructFns: (() => any)[] = [];
|
||||||
const env: Environment = getEnvironment();
|
const env: Environment = getEnvironment();
|
||||||
const scrollbarsOverlaid = env._nativeScrollbarIsOverlaid;
|
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 viewportElm = createDOM(`<div class="${classNameViewport} ${classNameViewportScrollbarStyling}"></div>`)[0];
|
||||||
const contentElm = createDOM(`<div class="${classNameContent}"></div>`)[0];
|
const contentElm = createDOM(`<div class="${classNameContent}"></div>`)[0];
|
||||||
|
|
||||||
const updateCache = createCache<StructureLifecycleCache>({
|
const { _options, _update, _cacheChange } = createLifecycleBase<StructureLifecycleOptions, StructureLifecycleCache>(
|
||||||
padding: [() => topRightBottomLeft(target, 'padding'), equalTRBL],
|
{
|
||||||
});
|
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') => {
|
const onSizeChanged = (direction?: 'ltr' | 'rtl') => {
|
||||||
updateCache('padding');
|
_cacheChange('padding');
|
||||||
};
|
};
|
||||||
const onTrinsicChanged = (widthIntrinsic: boolean, heightIntrinsic: boolean) => {
|
const onTrinsicChanged = (widthIntrinsic: boolean, heightIntrinsic: boolean) => {
|
||||||
console.log('heightAuot', heightIntrinsic);
|
console.log('heightAuot', heightIntrinsic); // eslint-disable-line
|
||||||
};
|
};
|
||||||
|
|
||||||
appendChildren(viewportElm, contentElm);
|
appendChildren(viewportElm, contentElm);
|
||||||
@@ -88,14 +86,8 @@ export const createStructureLifecycle = (target: HTMLElement, initialOptions?: S
|
|||||||
destructFns.push(createTrinsicObserver(target, onTrinsicChanged));
|
destructFns.push(createTrinsicObserver(target, onTrinsicChanged));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_options(newOptions?: StructureLifecycleOptions) {
|
_options,
|
||||||
// eslint-disable-next-line
|
_update,
|
||||||
console.log('_options', newOptions);
|
|
||||||
},
|
|
||||||
_update(force?: boolean) {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
console.log('_options', force);
|
|
||||||
},
|
|
||||||
_destruct() {
|
_destruct() {
|
||||||
runEach(destructFns);
|
runEach(destructFns);
|
||||||
removeElements(viewportElm);
|
removeElements(viewportElm);
|
||||||
|
|||||||
@@ -1,7 +1,75 @@
|
|||||||
|
import {
|
||||||
|
CacheUpdateInfo,
|
||||||
|
CachePropsToUpdate,
|
||||||
|
CacheChanged,
|
||||||
|
OptionsWithOptionsTemplate,
|
||||||
|
OptionsValidated,
|
||||||
|
transformOptions,
|
||||||
|
validateOptions,
|
||||||
|
assignDeep,
|
||||||
|
createCache,
|
||||||
|
} from 'support';
|
||||||
import { PlainObject } from 'typings';
|
import { PlainObject } from 'typings';
|
||||||
|
|
||||||
export interface Lifecycle<T = PlainObject> {
|
interface LifecycleUpdateHints<O, C> {
|
||||||
_options(options?: T): void;
|
_force?: boolean;
|
||||||
|
_changedOptions?: OptionsValidated<O>;
|
||||||
|
_changedCache?: CachePropsToUpdate<C>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AbstractLifecycle<O extends PlainObject> {
|
||||||
|
_options(newOptions?: O): O;
|
||||||
_update(force?: boolean): void;
|
_update(force?: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Lifecycle<T extends PlainObject> extends AbstractLifecycle<T> {
|
||||||
_destruct(): void;
|
_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 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 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 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> = {
|
export type CacheChanged<T> = {
|
||||||
[P in keyof T]?: T[P];
|
[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.
|
* @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.
|
* 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 cache: Cache<T> = {} as T;
|
||||||
const allProps: Array<keyof T> = keys(cacheUpdateInfo) as Array<keyof 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 result;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (propsToUpdate?: PropsToUpdate<T>, force?: boolean) => {
|
return (propsToUpdate?: CachePropsToUpdate<T>, force?: boolean) => {
|
||||||
const finalPropsToUpdate: Array<keyof T> =
|
const finalPropsToUpdate: Array<keyof T> =
|
||||||
(isString(propsToUpdate) ? ([propsToUpdate] as Array<keyof T>) : (propsToUpdate as Array<keyof T>)) || allProps;
|
(isString(propsToUpdate) ? ([propsToUpdate] as Array<keyof T>) : (propsToUpdate as Array<keyof T>)) || allProps;
|
||||||
each(finalPropsToUpdate, (prop) => {
|
each(finalPropsToUpdate, (prop) => {
|
||||||
|
|||||||
@@ -19,9 +19,12 @@ export type OptionsTemplate<T extends Required<T>> = {
|
|||||||
? OptionsTemplateValue<T[P]>
|
? OptionsTemplateValue<T[P]>
|
||||||
: never;
|
: never;
|
||||||
};
|
};
|
||||||
export type OptionsValidatedResult<T> = {
|
export type OptionsValidated<T> = {
|
||||||
|
[P in keyof T]?: T[P];
|
||||||
|
};
|
||||||
|
export type OptionsValidationResult<T> = {
|
||||||
readonly _foreign: PlainObject;
|
readonly _foreign: PlainObject;
|
||||||
readonly _validated: T;
|
readonly _validated: OptionsValidated<T>;
|
||||||
};
|
};
|
||||||
// Options With Options Template Typings:
|
// Options With Options Template Typings:
|
||||||
export type OptionsWithOptionsTemplateValue<T extends OptionsTemplateNativeTypes> = [T, OptionsTemplateValue<T>];
|
export type OptionsWithOptionsTemplateValue<T extends OptionsTemplateNativeTypes> = [T, OptionsTemplateValue<T>];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { each, indexOf, hasOwnProperty, keys } from 'support/utils';
|
import { each, indexOf, hasOwnProperty, keys } from 'support/utils';
|
||||||
import { type, isArray, isUndefined, isEmptyObject, isPlainObject, isString } from 'support/utils/types';
|
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';
|
import { PlainObject } from 'typings';
|
||||||
|
|
||||||
const { stringify } = JSON;
|
const { stringify } = JSON;
|
||||||
@@ -46,8 +46,8 @@ const validateRecursive = <T extends PlainObject>(
|
|||||||
optionsDiff: T,
|
optionsDiff: T,
|
||||||
doWriteErrors?: boolean,
|
doWriteErrors?: boolean,
|
||||||
propPath?: string
|
propPath?: string
|
||||||
): OptionsValidatedResult<T> => {
|
): OptionsValidationResult<T> => {
|
||||||
const validatedOptions: T = {} as T;
|
const validatedOptions: OptionsValidated<T> = {};
|
||||||
const optionsCopy: T = { ...options };
|
const optionsCopy: T = { ...options };
|
||||||
const props = keys(template).filter((prop) => hasOwnProperty(options, prop));
|
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 the template has a object as value, it means that the options are complex (verschachtelt)
|
||||||
if (templateIsComplex && isPlainObject(optionsValue)) {
|
if (templateIsComplex && isPlainObject(optionsValue)) {
|
||||||
const validatedResult = validateRecursive(optionsValue, templateValue as PlainObject, optionsDiffValue, doWriteErrors, propPrefix + prop);
|
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;
|
optionsCopy[prop] = validatedResult._foreign as any;
|
||||||
|
|
||||||
each([optionsCopy, validatedOptions], (value) => {
|
each([optionsCopy, validatedOptions], (value) => {
|
||||||
@@ -145,7 +145,7 @@ const validateOptions = <T extends PlainObject>(
|
|||||||
template: OptionsTemplate<Required<T>>,
|
template: OptionsTemplate<Required<T>>,
|
||||||
optionsDiff?: T,
|
optionsDiff?: T,
|
||||||
doWriteErrors?: boolean
|
doWriteErrors?: boolean
|
||||||
): OptionsValidatedResult<T> => {
|
): OptionsValidationResult<T> => {
|
||||||
/*
|
/*
|
||||||
if (!isEmptyObject(foreign) && doWriteErrors)
|
if (!isEmptyObject(foreign) && doWriteErrors)
|
||||||
console.warn(`The following options are discarded due to invalidity:\r\n ${window.JSON.stringify(foreign, null, 2)}`);
|
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';
|
import { PlainObject } from 'typings';
|
||||||
declare const optionsTemplateTypes: OptionsTemplateTypesDictionary;
|
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 };
|
export { validate, optionsTemplateTypes };
|
||||||
declare type OptionsTemplateTypesDictionary = {
|
declare type OptionsTemplateTypesDictionary = {
|
||||||
readonly boolean: OptionsTemplateType<boolean>;
|
readonly boolean: OptionsTemplateType<boolean>;
|
||||||
|
|||||||
Reference in New Issue
Block a user