vscroll
Version:
Virtual scroll engine
1,703 lines (1,680 loc) • 171 kB
JavaScript
/**
* vscroll (https://github.com/dhilt/vscroll) FESM2015
* Version: 1.8.0 (2025-11-28T14:24:43.799Z)
* Author: Denis Hilt
* License: MIT
*/
class Reactive {
constructor(value, options) {
this.id = 0;
if (value !== void 0) {
this.value = value;
this.initialValue = value;
}
this.options = options || {};
this.subscriptions = new Map();
}
set(value) {
if (this.value === value && !this.options.emitEqual) {
return;
}
this.value = value;
for (const [, sub] of this.subscriptions) {
sub.emit(value);
if (this.value !== value) {
break;
}
}
}
get() {
return this.value;
}
on(func) {
const id = this.id++;
const subscription = {
emit: func,
off: () => {
subscription.emit = () => null;
this.subscriptions.delete(id);
}
};
this.subscriptions.set(id, subscription);
if (this.options.emitOnSubscribe) {
subscription.emit(this.value);
}
return () => subscription.off();
}
once(func) {
const off = this.on(v => {
off();
func(v);
});
return off;
}
reset() {
this.set(this.initialValue);
}
dispose() {
this.subscriptions.forEach(sub => sub.off());
}
}
var AdapterPropName;
(function (AdapterPropName) {
AdapterPropName["id"] = "id";
AdapterPropName["mock"] = "mock";
AdapterPropName["augmented"] = "augmented";
AdapterPropName["version"] = "version";
AdapterPropName["init"] = "init";
AdapterPropName["init$"] = "init$";
AdapterPropName["packageInfo"] = "packageInfo";
AdapterPropName["itemsCount"] = "itemsCount";
AdapterPropName["bufferInfo"] = "bufferInfo";
AdapterPropName["isLoading"] = "isLoading";
AdapterPropName["isLoading$"] = "isLoading$";
AdapterPropName["loopPending"] = "loopPending";
AdapterPropName["loopPending$"] = "loopPending$";
AdapterPropName["firstVisible"] = "firstVisible";
AdapterPropName["firstVisible$"] = "firstVisible$";
AdapterPropName["lastVisible"] = "lastVisible";
AdapterPropName["lastVisible$"] = "lastVisible$";
AdapterPropName["bof"] = "bof";
AdapterPropName["bof$"] = "bof$";
AdapterPropName["eof"] = "eof";
AdapterPropName["eof$"] = "eof$";
AdapterPropName["paused"] = "paused";
AdapterPropName["paused$"] = "paused$";
AdapterPropName["reset"] = "reset";
AdapterPropName["reload"] = "reload";
AdapterPropName["append"] = "append";
AdapterPropName["prepend"] = "prepend";
AdapterPropName["check"] = "check";
AdapterPropName["remove"] = "remove";
AdapterPropName["clip"] = "clip";
AdapterPropName["insert"] = "insert";
AdapterPropName["replace"] = "replace";
AdapterPropName["update"] = "update";
AdapterPropName["pause"] = "pause";
AdapterPropName["resume"] = "resume";
AdapterPropName["fix"] = "fix";
AdapterPropName["relax"] = "relax";
AdapterPropName["showLog"] = "showLog";
})(AdapterPropName || (AdapterPropName = {}));
var AdapterPropType;
(function (AdapterPropType) {
AdapterPropType[AdapterPropType["Scalar"] = 0] = "Scalar";
AdapterPropType[AdapterPropType["Reactive"] = 1] = "Reactive";
AdapterPropType[AdapterPropType["WorkflowRunner"] = 2] = "WorkflowRunner";
AdapterPropType[AdapterPropType["Function"] = 3] = "Function";
})(AdapterPropType || (AdapterPropType = {}));
const Name = AdapterPropName;
const Type = AdapterPropType;
const noop = () => null;
const methodPreResult = {
immediate: true,
success: true,
details: 'Adapter is not initialized'
};
const methodPausedResult = {
immediate: true,
success: true,
details: 'Scroller is paused'
};
const noopWF = () => Promise.resolve(methodPreResult);
const emptyPackageInfo = {
core: {
name: '',
version: ''
},
consumer: {
name: '',
version: ''
}
};
const bufferInfoDefault = {
firstIndex: NaN,
lastIndex: NaN,
minIndex: NaN,
maxIndex: NaN,
absMinIndex: -Infinity,
absMaxIndex: +Infinity,
defaultSize: NaN
};
const EMPTY_ITEM = {
data: {},
element: {}
};
const getDefaultAdapterProps = () => [
{
type: Type.Scalar,
name: Name.id,
value: 0,
permanent: true
},
{
type: Type.Scalar,
name: Name.mock,
value: true,
permanent: true
},
{
type: Type.Scalar,
name: Name.augmented,
value: false,
permanent: true
},
{
type: Type.Scalar,
name: Name.version,
value: '',
permanent: true
},
{
type: Type.Scalar,
name: Name.init,
value: false,
reactive: Name.init$
},
{
type: Type.Scalar,
name: Name.packageInfo,
value: emptyPackageInfo,
onDemand: true
},
{
type: Type.Scalar,
name: Name.itemsCount,
value: 0,
onDemand: true
},
{
type: Type.Scalar,
name: Name.bufferInfo,
value: bufferInfoDefault,
onDemand: true
},
{
type: Type.Scalar,
name: Name.isLoading,
value: false,
reactive: Name.isLoading$
},
{
type: Type.Scalar,
name: Name.loopPending,
value: false,
reactive: Name.loopPending$
},
{
type: Type.Scalar,
name: Name.firstVisible,
value: EMPTY_ITEM,
reactive: Name.firstVisible$,
wanted: true
},
{
type: Type.Scalar,
name: Name.lastVisible,
value: EMPTY_ITEM,
reactive: Name.lastVisible$,
wanted: true
},
{
type: Type.Scalar,
name: Name.bof,
value: false,
reactive: Name.bof$
},
{
type: Type.Scalar,
name: Name.eof,
value: false,
reactive: Name.eof$
},
{
type: Type.Scalar,
name: Name.paused,
value: false,
reactive: Name.paused$
},
{
type: Type.WorkflowRunner,
name: Name.reset,
value: noopWF,
allowedWhenPaused: true
},
{
type: Type.WorkflowRunner,
name: Name.reload,
value: noopWF
},
{
type: Type.WorkflowRunner,
name: Name.append,
value: noopWF
},
{
type: Type.WorkflowRunner,
name: Name.prepend,
value: noopWF
},
{
type: Type.WorkflowRunner,
name: Name.check,
value: noopWF
},
{
type: Type.WorkflowRunner,
name: Name.remove,
value: noopWF
},
{
type: Type.WorkflowRunner,
name: Name.clip,
value: noopWF
},
{
type: Type.WorkflowRunner,
name: Name.insert,
value: noopWF
},
{
type: Type.WorkflowRunner,
name: Name.replace,
value: noopWF
},
{
type: Type.WorkflowRunner,
name: Name.update,
value: noopWF
},
{
type: Type.WorkflowRunner,
name: Name.pause,
value: noopWF
},
{
type: Type.WorkflowRunner,
name: Name.resume,
value: noopWF,
allowedWhenPaused: true
},
{
type: Type.WorkflowRunner,
name: Name.fix,
value: noopWF
},
{
type: Type.Function,
name: Name.relax,
value: noop
},
{
type: Type.Function,
name: Name.showLog,
value: noop
},
{
type: Type.Reactive,
name: Name.init$,
value: new Reactive()
},
{
type: Type.Reactive,
name: Name.isLoading$,
value: new Reactive()
},
{
type: Type.Reactive,
name: Name.loopPending$,
value: new Reactive()
},
{
type: Type.Reactive,
name: Name.firstVisible$,
value: new Reactive(EMPTY_ITEM, { emitOnSubscribe: true }),
wanted: true
},
{
type: Type.Reactive,
name: Name.lastVisible$,
value: new Reactive(EMPTY_ITEM, { emitOnSubscribe: true }),
wanted: true
},
{
type: Type.Reactive,
name: Name.bof$,
value: new Reactive()
},
{
type: Type.Reactive,
name: Name.eof$,
value: new Reactive()
},
{
type: Type.Reactive,
name: Name.paused$,
value: new Reactive()
}
];
const reactiveConfigStorage = new Map();
var core = {
name: 'vscroll',
version: '1.8.0'
};
const getBox = (id) => {
var _a;
return (_a = wantedStorage.get(id || -1)) === null || _a === void 0 ? void 0 : _a.box;
};
const setBox = ({ name, wanted }, id) => {
const Wanted = wantedStorage.get(id || -1);
if (wanted && Wanted && !Wanted.box[name] && !Wanted.block) {
const { firstVisible: a, firstVisible$: a$ } = AdapterPropName;
const { lastVisible: b, lastVisible$: b$ } = AdapterPropName;
Wanted.box[a] = Wanted.box[a$] = [a, a$].some(n => n === name) || Wanted.box[a];
Wanted.box[b] = Wanted.box[b$] = [b, b$].some(n => n === name) || Wanted.box[b];
return true;
}
return false;
};
const setBlock = (value, id) => {
const Wanted = wantedStorage.get(id || -1);
if (Wanted) {
Wanted.block = value;
}
};
const wantedUtils = {
getBox,
setBox,
setBlock
};
const wantedStorage = new Map();
let instanceCount$1 = 0;
class AdapterContext {
constructor(config) {
const { mock, reactive } = config;
const id = ++instanceCount$1;
const conf = { configurable: true };
const reactivePropsStore = {};
wantedStorage.set(id, { box: {}, block: false });
// set up permanent props
Object.defineProperty(this, AdapterPropName.id, Object.assign({ get: () => id }, conf));
Object.defineProperty(this, AdapterPropName.mock, Object.assign({ get: () => mock }, conf));
Object.defineProperty(this, AdapterPropName.augmented, Object.assign({ get: () => false }, conf));
Object.defineProperty(this, AdapterPropName.version, Object.assign({ get: () => core.version }, conf));
// set up default props, they will be reassigned during the Adapter instantiation
getDefaultAdapterProps()
.filter(({ permanent }) => !permanent)
.forEach(prop => {
let { value } = prop;
// reactive props might be reconfigured by the vscroll consumer
if (reactive && prop.type === AdapterPropType.Reactive) {
const react = reactive[prop.name];
if (react) {
// here we have a configured reactive property that came from the outer config
// this prop must be exposed via Adapter, but at the same time we need to
// persist the original default value as it will be used by the Adapter internally
reactivePropsStore[prop.name] = Object.assign(Object.assign({}, react), { default: value // persisting the default native Reactive prop
});
value = react.source; // exposing the configured prop instead of the default one
}
}
Object.defineProperty(this, prop.name, Object.assign({ get: () => {
wantedUtils.setBox(prop, id);
return value;
} }, conf));
});
if (reactive) {
// save both configured and default reactive props in the store
reactiveConfigStorage.set(id, reactivePropsStore);
}
}
}
const getDefaultAdapterConfig = () => {
const reactive = getDefaultAdapterProps()
.filter(({ type }) => type === AdapterPropType.Reactive)
.reduce((acc, { name, value }) => {
acc[name] = {
source: value,
emit: (source, val) => source.set(val)
};
return acc;
}, {});
return { mock: false, reactive };
};
class DatasourceGeneric {
constructor(datasource, config) {
this.get = datasource.get;
this.settings = datasource.settings;
this.devSettings = datasource.devSettings;
const adapterContext = new AdapterContext(config || { mock: false });
this.adapter = adapterContext;
}
// todo: should it be published?
dispose() {
reactiveConfigStorage.delete(this.adapter.id);
wantedStorage.delete(this.adapter.id);
}
}
const makeDatasource = (getAdapterConfig) => class extends DatasourceGeneric {
constructor(datasource) {
const config = typeof getAdapterConfig === 'function' ? getAdapterConfig() : getDefaultAdapterConfig();
super(datasource, config);
}
};
const Datasource = makeDatasource();
var Direction;
(function (Direction) {
Direction["forward"] = "forward";
Direction["backward"] = "backward";
})(Direction || (Direction = {}));
var SizeStrategy;
(function (SizeStrategy) {
SizeStrategy["Average"] = "average";
SizeStrategy["Constant"] = "constant";
SizeStrategy["Frequent"] = "frequent";
})(SizeStrategy || (SizeStrategy = {}));
var ValidatorType;
(function (ValidatorType) {
ValidatorType["number"] = "must be a number";
ValidatorType["integer"] = "must be an integer";
ValidatorType["integerUnlimited"] = "must be an integer or infinity";
ValidatorType["moreOrEqual"] = "must be a number greater than (or equal to) {arg1}";
ValidatorType["itemList"] = "must be an array of items {arg1}";
ValidatorType["boolean"] = "must be a boolean";
ValidatorType["object"] = "must be an object";
ValidatorType["element"] = "must be an html element";
ValidatorType["function"] = "must be a function";
ValidatorType["funcOfxArguments"] = "must have {arg1} argument(s)";
ValidatorType["funcOfxAndMoreArguments"] = "must have at least {arg1} argument(s)";
ValidatorType["funcOfXToYArguments"] = "must have {arg1} to {arg2} arguments";
ValidatorType["oneOfCan"] = "can be present as only one item of {arg1} list";
ValidatorType["oneOfMust"] = "must be present as only one item of {arg1} list";
ValidatorType["or"] = "must satisfy at least 1 validator from {arg1} list";
ValidatorType["enum"] = "must belong to {arg1} list";
})(ValidatorType || (ValidatorType = {}));
const getError = (msg, args) => (args || ['']).reduce((acc, arg, index) => acc.replace(`{arg${index + 1}}`, arg), msg);
const getNumber = (value) => typeof value === 'number' || (typeof value === 'string' && value !== '') ? Number(value) : NaN;
const onNumber = (value) => {
const parsedValue = getNumber(value);
const errors = [];
if (Number.isNaN(parsedValue)) {
errors.push(ValidatorType.number);
}
return { value: parsedValue, isSet: true, isValid: !errors.length, errors };
};
const onInteger = (value) => {
const errors = [];
value = getNumber(value);
const parsedValue = parseInt(String(value), 10);
if (value !== parsedValue) {
errors.push(ValidatorType.integer);
}
return { value: parsedValue, isSet: true, isValid: !errors.length, errors };
};
const onIntegerUnlimited = (value) => {
let parsedValue = value;
const errors = [];
value = getNumber(value);
if (!Number.isFinite(value)) {
parsedValue = value;
}
else {
parsedValue = parseInt(String(value), 10);
}
if (value !== parsedValue) {
errors.push(ValidatorType.integerUnlimited);
}
return { value: parsedValue, isSet: true, isValid: !errors.length, errors };
};
const onMoreOrEqual = (limit, fallback) => (value) => {
const result = onNumber(value);
if (!result.isValid) {
return result;
}
let parsedValue = result.value;
const errors = [];
if (parsedValue < limit) {
if (!fallback) {
errors.push(getError(ValidatorType.moreOrEqual, [String(limit)]));
}
else {
parsedValue = limit;
}
}
return { value: parsedValue, isSet: true, isValid: !errors.length, errors };
};
const onBoolean = (value) => {
const errors = [];
let parsedValue = value;
if (value === 'true') {
parsedValue = true;
}
else if (value === 'false') {
parsedValue = false;
}
if (typeof parsedValue !== 'boolean') {
errors.push(ValidatorType.boolean);
}
return { value: parsedValue, isSet: true, isValid: !errors.length, errors };
};
const onObject = (value) => {
const errors = [];
if (!value || Object.prototype.toString.call(value) !== '[object Object]') {
errors.push(ValidatorType.object);
}
return { value, isSet: true, isValid: !errors.length, errors };
};
const onHtmlElement = (value) => {
const errors = [];
if (!(value instanceof Element) && !(value instanceof Document)) {
errors.push(ValidatorType.element);
}
return { value, isSet: true, isValid: !errors.length, errors };
};
const onItemList = (value) => {
let parsedValue = value;
const errors = [];
if (!Array.isArray(value)) {
errors.push(ValidatorType.itemList);
parsedValue = [];
}
else if (!value.length) {
errors.push(getError(ValidatorType.itemList, ['with at least 1 item']));
}
else if (value.length > 1) {
const type = typeof value[0];
for (let i = value.length - 1; i >= 0; i--) {
if (typeof value[i] !== type) {
errors.push(getError(ValidatorType.itemList, ['of items of the same type']));
break;
}
}
}
return { value: parsedValue, isSet: true, isValid: !errors.length, errors };
};
const onFunction = (value) => {
const errors = [];
if (typeof value !== 'function') {
errors.push(ValidatorType.function);
}
return { value: value, isSet: true, isValid: !errors.length, errors };
};
const onFunctionWithXArguments = (argsCount) => (value) => {
const result = onFunction(value);
if (!result.isValid) {
return result;
}
value = result.value;
const errors = [];
if (value.length !== argsCount) {
errors.push(getError(ValidatorType.funcOfxArguments, [String(argsCount)]));
}
return { value: value, isSet: true, isValid: !errors.length, errors };
};
const onFunctionWithXAndMoreArguments = (argsCount) => (value) => {
const result = onFunction(value);
if (!result.isValid) {
return result;
}
value = result.value;
const errors = [];
if (value.length < argsCount) {
errors.push(getError(ValidatorType.funcOfxArguments, [String(argsCount)]));
}
return { value: value, isSet: true, isValid: !errors.length, errors };
};
const onFunctionWithXToYArguments = (from, to) => (value) => {
const result = onFunction(value);
if (!result.isValid) {
return result;
}
value = result.value;
const errors = [];
if (value.length < from || value.length > to) {
errors.push(getError(ValidatorType.funcOfXToYArguments, [String(from), String(to)]));
}
return { value: value, isSet: true, isValid: !errors.length, errors };
};
const onOneOf = (tokens, must) => (value, context) => {
const errors = [];
const isSet = value !== void 0;
let noOneIsPresent = !isSet;
const err = must ? ValidatorType.oneOfMust : ValidatorType.oneOfCan;
if (!Array.isArray(tokens) || !tokens.length) {
errors.push(getError(err, ['undefined']));
}
else {
for (let i = tokens.length - 1; i >= 0; i--) {
const token = tokens[i];
if (typeof token !== 'string') {
errors.push(getError(err, [tokens.join('", "')]) + ' (non-string token)');
break;
}
const isAnotherPresent = context && Object.prototype.hasOwnProperty.call(context, token);
if (isSet && isAnotherPresent) {
errors.push(getError(err, [tokens.join('", "')]) + ` (${token} is present)`);
break;
}
if (noOneIsPresent && isAnotherPresent) {
noOneIsPresent = false;
}
}
if (must && noOneIsPresent) {
errors.push(getError(err, [tokens.join('", "')]));
}
}
return { value, isSet, isValid: !errors.length, errors };
};
const onOr = (validators) => (value) => {
const errors = [];
if (validators.every(validator => !validator.method(value).isValid)) {
errors.push(validators.map(v => v.type).join(' OR '));
}
return { value, isSet: true, isValid: !errors.length, errors };
};
const onEnum = (list) => (value) => {
const errors = [];
const values = Object.keys(list)
.filter(k => isNaN(Number(k)))
.map(k => list[k]);
if (!values.some(item => item === value)) {
errors.push(getError(ValidatorType.enum, ['[' + values.join(',') + ']']));
}
return { value, isSet: true, isValid: !errors.length, errors };
};
const VALIDATORS = {
NUMBER: {
type: ValidatorType.number,
method: onNumber
},
INTEGER: {
type: ValidatorType.integer,
method: onInteger
},
INTEGER_UNLIMITED: {
type: ValidatorType.integerUnlimited,
method: onIntegerUnlimited
},
MORE_OR_EQUAL: (limit, fallback) => ({
type: ValidatorType.moreOrEqual,
method: onMoreOrEqual(limit, fallback)
}),
BOOLEAN: {
type: ValidatorType.boolean,
method: onBoolean
},
OBJECT: {
type: ValidatorType.object,
method: onObject
},
ITEM_LIST: {
type: ValidatorType.itemList,
method: onItemList
},
ELEMENT: {
type: ValidatorType.element,
method: onHtmlElement
},
FUNC: {
type: ValidatorType.function,
method: onFunction
},
FUNC_WITH_X_ARGUMENTS: (count) => ({
type: ValidatorType.funcOfxArguments,
method: onFunctionWithXArguments(count)
}),
FUNC_WITH_X_AND_MORE_ARGUMENTS: (count) => ({
type: ValidatorType.funcOfxAndMoreArguments,
method: onFunctionWithXAndMoreArguments(count)
}),
FUNC_WITH_X_TO_Y_ARGUMENTS: (from, to) => ({
type: ValidatorType.funcOfXToYArguments,
method: onFunctionWithXToYArguments(from, to)
}),
ONE_OF_CAN: (list) => ({
type: ValidatorType.oneOfCan,
method: onOneOf(list, false)
}),
ONE_OF_MUST: (list) => ({
type: ValidatorType.oneOfMust,
method: onOneOf(list, true)
}),
OR: (list) => ({
type: ValidatorType.or,
method: onOr(list)
}),
ENUM: (list) => ({
type: ValidatorType.enum,
method: onEnum(list)
})
};
class ValidatedData {
constructor(context) {
this.params = {};
this.contextErrors = [];
this.errors = [];
this.isValid = true;
this.setContext(context);
}
setContext(context) {
if (!context || Object.prototype.toString.call(context) !== '[object Object]') {
this.setCommonError('context is not an object');
this.isValidContext = false;
}
else {
this.isValidContext = true;
}
this.context = context;
}
setValidity() {
this.errors = Object.keys(this.params).reduce((acc, key) => [...acc, ...this.params[key].errors], []);
this.isValid = !this.errors.length;
}
setCommonError(error) {
this.contextErrors.push(error);
this.errors.push(error);
this.isValid = false;
}
setParam(token, value) {
if (!value.isValid) {
value.errors = !value.isSet
? [`"${token}" must be set`]
: value.errors.map((err) => `"${token}" ${err}`);
}
this.params[token] = value;
this.setValidity();
}
showErrors() {
return this.errors.length ? 'validation failed: ' + this.errors.join(', ') : '';
}
}
const runValidator = (current, validator, context) => {
const { value, errors } = current;
const result = validator.method(value, context);
const _errors = [...errors, ...result.errors];
return {
value: result.value,
isSet: result.isSet,
isValid: !_errors.length,
errors: _errors
};
};
const getDefault = (value, prop) => {
const empty = value === void 0;
const auto = !prop.mandatory && prop.defaultValue !== void 0;
return {
value: !empty ? value : auto ? prop.defaultValue : void 0,
isSet: !empty || auto,
isValid: !empty || !prop.mandatory,
errors: []
};
};
const validateOne = (context, name, prop) => {
const result = getDefault(context[name], prop);
if (!result.isSet) {
const oneOfMust = prop.validators.find(v => v.type === ValidatorType.oneOfMust);
if (oneOfMust) {
return runValidator(result, oneOfMust, context);
}
}
else {
for (const validator of Object.values(prop.validators)) {
const current = runValidator(result, validator, context);
if (!current.isValid && prop.defaultValue !== void 0) {
return {
value: prop.defaultValue,
isSet: true,
isValid: true,
errors: []
};
}
Object.assign(result, current);
}
}
return result;
};
const validate = (context, params) => {
const data = new ValidatedData(context);
Object.entries(params).forEach(([key, prop]) => data.setParam(key, data.isValidContext ? validateOne(data.context, key, prop) : getDefault(void 0, prop)));
return data;
};
const { OBJECT: OBJECT$2, FUNC_WITH_X_AND_MORE_ARGUMENTS: FUNC_WITH_X_AND_MORE_ARGUMENTS$1 } = VALIDATORS;
var DatasourceProps;
(function (DatasourceProps) {
DatasourceProps["get"] = "get";
DatasourceProps["settings"] = "settings";
DatasourceProps["devSettings"] = "devSettings";
})(DatasourceProps || (DatasourceProps = {}));
const DATASOURCE = {
[DatasourceProps.get]: {
validators: [FUNC_WITH_X_AND_MORE_ARGUMENTS$1(2)],
mandatory: true
},
[DatasourceProps.settings]: {
validators: [OBJECT$2]
},
[DatasourceProps.devSettings]: {
validators: [OBJECT$2]
}
};
const { NUMBER, INTEGER: INTEGER$1, INTEGER_UNLIMITED: INTEGER_UNLIMITED$1, MORE_OR_EQUAL, BOOLEAN: BOOLEAN$1, ELEMENT: ELEMENT$1, FUNC: FUNC$1, OR: OR$1, ENUM } = VALIDATORS;
var Settings$1;
(function (Settings) {
Settings["adapter"] = "adapter";
Settings["startIndex"] = "startIndex";
Settings["minIndex"] = "minIndex";
Settings["maxIndex"] = "maxIndex";
Settings["itemSize"] = "itemSize";
Settings["bufferSize"] = "bufferSize";
Settings["padding"] = "padding";
Settings["infinite"] = "infinite";
Settings["horizontal"] = "horizontal";
Settings["windowViewport"] = "windowViewport";
Settings["viewportElement"] = "viewportElement";
Settings["inverse"] = "inverse";
Settings["onBeforeClip"] = "onBeforeClip";
Settings["sizeStrategy"] = "sizeStrategy";
})(Settings$1 || (Settings$1 = {}));
var DevSettings;
(function (DevSettings) {
DevSettings["debug"] = "debug";
DevSettings["immediateLog"] = "immediateLog";
DevSettings["logProcessRun"] = "logProcessRun";
DevSettings["logTime"] = "logTime";
DevSettings["logColor"] = "logColor";
DevSettings["throttle"] = "throttle";
DevSettings["initDelay"] = "initDelay";
DevSettings["initWindowDelay"] = "initWindowDelay";
DevSettings["cacheData"] = "cacheData";
DevSettings["cacheOnReload"] = "cacheOnReload";
DevSettings["dismissOverflowAnchor"] = "dismissOverflowAnchor";
DevSettings["directionPriority"] = "directionPriority";
})(DevSettings || (DevSettings = {}));
const MIN = {
[Settings$1.itemSize]: 1,
[Settings$1.bufferSize]: 1,
[Settings$1.padding]: 0.01,
[DevSettings.throttle]: 0,
[DevSettings.initDelay]: 0,
[DevSettings.initWindowDelay]: 0
};
const SETTINGS = {
[Settings$1.adapter]: {
validators: [BOOLEAN$1],
defaultValue: false
},
[Settings$1.startIndex]: {
validators: [INTEGER$1],
defaultValue: 1
},
[Settings$1.minIndex]: {
validators: [INTEGER_UNLIMITED$1],
defaultValue: -Infinity
},
[Settings$1.maxIndex]: {
validators: [INTEGER_UNLIMITED$1],
defaultValue: Infinity
},
[Settings$1.itemSize]: {
validators: [INTEGER$1, MORE_OR_EQUAL(MIN[Settings$1.itemSize], true)],
defaultValue: NaN
},
[Settings$1.bufferSize]: {
validators: [INTEGER$1, MORE_OR_EQUAL(MIN[Settings$1.bufferSize], true)],
defaultValue: 5
},
[Settings$1.padding]: {
validators: [NUMBER, MORE_OR_EQUAL(MIN[Settings$1.padding], true)],
defaultValue: 0.5
},
[Settings$1.infinite]: {
validators: [BOOLEAN$1],
defaultValue: false
},
[Settings$1.horizontal]: {
validators: [BOOLEAN$1],
defaultValue: false
},
[Settings$1.windowViewport]: {
validators: [BOOLEAN$1],
defaultValue: false
},
[Settings$1.viewportElement]: {
validators: [OR$1([ELEMENT$1, FUNC$1])],
defaultValue: null
},
[Settings$1.inverse]: {
validators: [BOOLEAN$1],
defaultValue: false
},
[Settings$1.onBeforeClip]: {
validators: [FUNC$1],
defaultValue: null
},
[Settings$1.sizeStrategy]: {
validators: [ENUM(SizeStrategy)],
defaultValue: SizeStrategy.Average
}
};
const DEV_SETTINGS = {
[DevSettings.debug]: {
validators: [BOOLEAN$1],
defaultValue: false
},
[DevSettings.immediateLog]: {
validators: [BOOLEAN$1],
defaultValue: true
},
[DevSettings.logProcessRun]: {
validators: [BOOLEAN$1],
defaultValue: false
},
[DevSettings.logTime]: {
validators: [BOOLEAN$1],
defaultValue: false
},
[DevSettings.logColor]: {
validators: [BOOLEAN$1],
defaultValue: true
},
[DevSettings.throttle]: {
validators: [INTEGER$1, MORE_OR_EQUAL(MIN[DevSettings.throttle], true)],
defaultValue: 40
},
[DevSettings.initDelay]: {
validators: [INTEGER$1, MORE_OR_EQUAL(MIN[DevSettings.initDelay], true)],
defaultValue: 1
},
[DevSettings.initWindowDelay]: {
validators: [INTEGER$1, MORE_OR_EQUAL(MIN[DevSettings.initWindowDelay], true)],
defaultValue: 40
},
[DevSettings.cacheData]: {
validators: [BOOLEAN$1],
defaultValue: false
},
[DevSettings.cacheOnReload]: {
validators: [BOOLEAN$1],
defaultValue: false
},
[DevSettings.dismissOverflowAnchor]: {
validators: [BOOLEAN$1],
defaultValue: true
},
[DevSettings.directionPriority]: {
validators: [ENUM(Direction)],
defaultValue: Direction.backward
}
};
var CommonProcess;
(function (CommonProcess) {
CommonProcess["init"] = "init";
CommonProcess["scroll"] = "scroll";
CommonProcess["start"] = "start";
CommonProcess["preFetch"] = "preFetch";
CommonProcess["fetch"] = "fetch";
CommonProcess["postFetch"] = "postFetch";
CommonProcess["render"] = "render";
CommonProcess["preClip"] = "preClip";
CommonProcess["clip"] = "clip";
CommonProcess["adjust"] = "adjust";
CommonProcess["end"] = "end";
})(CommonProcess || (CommonProcess = {}));
var AdapterProcess;
(function (AdapterProcess) {
AdapterProcess["reset"] = "adapter.reset";
AdapterProcess["reload"] = "adapter.reload";
AdapterProcess["append"] = "adapter.append";
AdapterProcess["prepend"] = "adapter.prepend";
AdapterProcess["check"] = "adapter.check";
AdapterProcess["remove"] = "adapter.remove";
AdapterProcess["replace"] = "adapter.replace";
AdapterProcess["update"] = "adapter.update";
AdapterProcess["clip"] = "adapter.clip";
AdapterProcess["insert"] = "adapter.insert";
AdapterProcess["pause"] = "adapter.pause";
AdapterProcess["fix"] = "adapter.fix";
})(AdapterProcess || (AdapterProcess = {}));
var ProcessStatus;
(function (ProcessStatus) {
ProcessStatus["start"] = "start";
ProcessStatus["next"] = "next";
ProcessStatus["done"] = "done";
ProcessStatus["error"] = "error";
})(ProcessStatus || (ProcessStatus = {}));
const { INTEGER, INTEGER_UNLIMITED, BOOLEAN, OBJECT: OBJECT$1, ITEM_LIST, FUNC_WITH_X_ARGUMENTS: FUNC_WITH_X_ARGUMENTS$1, FUNC_WITH_X_AND_MORE_ARGUMENTS, FUNC_WITH_X_TO_Y_ARGUMENTS, ONE_OF_MUST, ONE_OF_CAN, OR } = VALIDATORS;
var AdapterNoParams;
(function (AdapterNoParams) {
})(AdapterNoParams || (AdapterNoParams = {}));
const NO_METHOD_PARAMS = {};
const RESET_METHOD_PARAMS = {
[DatasourceProps.get]: {
validators: [FUNC_WITH_X_AND_MORE_ARGUMENTS(2)]
},
[DatasourceProps.settings]: {
validators: [OBJECT$1]
},
[DatasourceProps.devSettings]: {
validators: [OBJECT$1]
}
};
var AdapterReloadParams;
(function (AdapterReloadParams) {
AdapterReloadParams["reloadIndex"] = "reloadIndex";
})(AdapterReloadParams || (AdapterReloadParams = {}));
const RELOAD_METHOD_PARAMS = {
[AdapterReloadParams.reloadIndex]: {
validators: [INTEGER]
}
};
var AdapterPrependParams;
(function (AdapterPrependParams) {
AdapterPrependParams["items"] = "items";
AdapterPrependParams["bof"] = "bof";
AdapterPrependParams["increase"] = "increase";
AdapterPrependParams["virtualize"] = "virtualize";
})(AdapterPrependParams || (AdapterPrependParams = {}));
const PREPEND_METHOD_PARAMS = {
[AdapterPrependParams.items]: {
validators: [ITEM_LIST],
mandatory: true
},
[AdapterPrependParams.bof]: {
validators: [BOOLEAN, ONE_OF_CAN([AdapterPrependParams.virtualize])],
defaultValue: false
},
[AdapterPrependParams.increase]: {
validators: [BOOLEAN],
defaultValue: false
},
[AdapterPrependParams.virtualize]: {
validators: [BOOLEAN, ONE_OF_CAN([AdapterPrependParams.bof])],
defaultValue: false
}
};
var AdapterAppendParams;
(function (AdapterAppendParams) {
AdapterAppendParams["items"] = "items";
AdapterAppendParams["eof"] = "eof";
AdapterAppendParams["decrease"] = "decrease";
AdapterAppendParams["virtualize"] = "virtualize";
})(AdapterAppendParams || (AdapterAppendParams = {}));
const APPEND_METHOD_PARAMS = {
[AdapterAppendParams.items]: {
validators: [ITEM_LIST],
mandatory: true
},
[AdapterAppendParams.eof]: {
validators: [BOOLEAN, ONE_OF_CAN([AdapterAppendParams.virtualize])],
defaultValue: false
},
[AdapterAppendParams.decrease]: {
validators: [BOOLEAN],
defaultValue: false
},
[AdapterPrependParams.virtualize]: {
validators: [BOOLEAN, ONE_OF_CAN([AdapterAppendParams.eof])],
defaultValue: false
}
};
var AdapterRemoveParams;
(function (AdapterRemoveParams) {
AdapterRemoveParams["predicate"] = "predicate";
AdapterRemoveParams["indexes"] = "indexes";
AdapterRemoveParams["increase"] = "increase";
})(AdapterRemoveParams || (AdapterRemoveParams = {}));
const REMOVE_METHOD_PARAMS = {
[AdapterRemoveParams.predicate]: {
validators: [FUNC_WITH_X_ARGUMENTS$1(1), ONE_OF_MUST([AdapterRemoveParams.indexes])]
},
[AdapterRemoveParams.indexes]: {
validators: [ITEM_LIST, ONE_OF_MUST([AdapterRemoveParams.predicate])]
},
[AdapterRemoveParams.increase]: {
validators: [BOOLEAN],
defaultValue: false
}
};
var AdapterClipParams;
(function (AdapterClipParams) {
AdapterClipParams["backwardOnly"] = "backwardOnly";
AdapterClipParams["forwardOnly"] = "forwardOnly";
})(AdapterClipParams || (AdapterClipParams = {}));
const CLIP_METHOD_PARAMS = {
[AdapterClipParams.backwardOnly]: {
validators: [BOOLEAN, ONE_OF_CAN([AdapterClipParams.forwardOnly])],
defaultValue: false
},
[AdapterClipParams.forwardOnly]: {
validators: [BOOLEAN, ONE_OF_CAN([AdapterClipParams.backwardOnly])],
defaultValue: false
}
};
var AdapterInsertParams;
(function (AdapterInsertParams) {
AdapterInsertParams["items"] = "items";
AdapterInsertParams["before"] = "before";
AdapterInsertParams["after"] = "after";
AdapterInsertParams["beforeIndex"] = "beforeIndex";
AdapterInsertParams["afterIndex"] = "afterIndex";
AdapterInsertParams["decrease"] = "decrease";
})(AdapterInsertParams || (AdapterInsertParams = {}));
const INSERT_METHOD_PARAMS = {
[AdapterInsertParams.items]: {
validators: [ITEM_LIST],
mandatory: true
},
[AdapterInsertParams.before]: {
validators: [
FUNC_WITH_X_ARGUMENTS$1(1),
ONE_OF_MUST([
AdapterInsertParams.after,
AdapterInsertParams.beforeIndex,
AdapterInsertParams.afterIndex
])
]
},
[AdapterInsertParams.after]: {
validators: [
FUNC_WITH_X_ARGUMENTS$1(1),
ONE_OF_MUST([
AdapterInsertParams.before,
AdapterInsertParams.beforeIndex,
AdapterInsertParams.afterIndex
])
]
},
[AdapterInsertParams.beforeIndex]: {
validators: [
INTEGER,
ONE_OF_MUST([
AdapterInsertParams.before,
AdapterInsertParams.after,
AdapterInsertParams.afterIndex
])
]
},
[AdapterInsertParams.afterIndex]: {
validators: [
INTEGER,
ONE_OF_MUST([
AdapterInsertParams.before,
AdapterInsertParams.after,
AdapterInsertParams.beforeIndex
])
]
},
[AdapterInsertParams.decrease]: {
validators: [BOOLEAN],
defaultValue: false
}
};
var AdapterReplaceParams;
(function (AdapterReplaceParams) {
AdapterReplaceParams["items"] = "items";
AdapterReplaceParams["predicate"] = "predicate";
AdapterReplaceParams["fixRight"] = "fixRight";
})(AdapterReplaceParams || (AdapterReplaceParams = {}));
const REPLACE_METHOD_PARAMS = {
[AdapterInsertParams.items]: {
validators: [ITEM_LIST],
mandatory: true
},
[AdapterReplaceParams.predicate]: {
validators: [FUNC_WITH_X_ARGUMENTS$1(1)],
mandatory: true
},
[AdapterReplaceParams.fixRight]: {
validators: [BOOLEAN],
defaultValue: false
}
};
var AdapterUpdateParams;
(function (AdapterUpdateParams) {
AdapterUpdateParams["predicate"] = "predicate";
AdapterUpdateParams["fixRight"] = "fixRight";
})(AdapterUpdateParams || (AdapterUpdateParams = {}));
const UPDATE_METHOD_PARAMS = {
[AdapterUpdateParams.predicate]: {
validators: [FUNC_WITH_X_ARGUMENTS$1(1)],
mandatory: true
},
[AdapterUpdateParams.fixRight]: {
validators: [BOOLEAN],
defaultValue: false
}
};
var AdapterFixParams;
(function (AdapterFixParams) {
AdapterFixParams["scrollPosition"] = "scrollPosition";
AdapterFixParams["minIndex"] = "minIndex";
AdapterFixParams["maxIndex"] = "maxIndex";
AdapterFixParams["updater"] = "updater";
AdapterFixParams["scrollToItem"] = "scrollToItem";
AdapterFixParams["scrollToItemOpt"] = "scrollToItemOpt";
})(AdapterFixParams || (AdapterFixParams = {}));
const FIX_METHOD_PARAMS = {
[AdapterFixParams.scrollPosition]: {
validators: [INTEGER_UNLIMITED]
},
[AdapterFixParams.minIndex]: {
validators: [INTEGER_UNLIMITED]
},
[AdapterFixParams.maxIndex]: {
validators: [INTEGER_UNLIMITED]
},
[AdapterFixParams.updater]: {
validators: [FUNC_WITH_X_TO_Y_ARGUMENTS(1, 2)]
},
[AdapterFixParams.scrollToItem]: {
validators: [FUNC_WITH_X_ARGUMENTS$1(1)]
},
[AdapterFixParams.scrollToItemOpt]: {
validators: [OR([BOOLEAN, OBJECT$1])]
}
};
const AdapterMethods = {
[AdapterProcess.reset]: DatasourceProps,
[AdapterProcess.reload]: AdapterReloadParams,
[AdapterProcess.prepend]: AdapterPrependParams,
[AdapterProcess.append]: AdapterAppendParams,
[AdapterProcess.check]: AdapterNoParams,
[AdapterProcess.remove]: AdapterRemoveParams,
[AdapterProcess.clip]: AdapterClipParams,
[AdapterProcess.insert]: AdapterInsertParams,
[AdapterProcess.replace]: AdapterReplaceParams,
[AdapterProcess.update]: AdapterUpdateParams,
[AdapterProcess.pause]: AdapterNoParams,
[AdapterProcess.fix]: AdapterFixParams
};
const ADAPTER_METHODS = {
[AdapterProcess.reset]: RESET_METHOD_PARAMS,
[AdapterProcess.reload]: RELOAD_METHOD_PARAMS,
[AdapterProcess.prepend]: PREPEND_METHOD_PARAMS,
[AdapterProcess.append]: APPEND_METHOD_PARAMS,
[AdapterProcess.check]: NO_METHOD_PARAMS,
[AdapterProcess.remove]: REMOVE_METHOD_PARAMS,
[AdapterProcess.clip]: CLIP_METHOD_PARAMS,
[AdapterProcess.insert]: INSERT_METHOD_PARAMS,
[AdapterProcess.replace]: REPLACE_METHOD_PARAMS,
[AdapterProcess.update]: UPDATE_METHOD_PARAMS,
[AdapterProcess.pause]: NO_METHOD_PARAMS,
[AdapterProcess.fix]: FIX_METHOD_PARAMS
};
const { ELEMENT, OBJECT, FUNC, FUNC_WITH_X_ARGUMENTS } = VALIDATORS;
var WorkflowProps;
(function (WorkflowProps) {
WorkflowProps["consumer"] = "consumer";
WorkflowProps["element"] = "element";
WorkflowProps["datasource"] = "datasource";
WorkflowProps["run"] = "run";
WorkflowProps["Routines"] = "Routines";
})(WorkflowProps || (WorkflowProps = {}));
const WORKFLOW = {
[WorkflowProps.consumer]: {
validators: [OBJECT]
},
[WorkflowProps.element]: {
validators: [ELEMENT],
mandatory: true
},
[WorkflowProps.datasource]: {
validators: [OBJECT],
mandatory: true
},
[WorkflowProps.run]: {
validators: [FUNC_WITH_X_ARGUMENTS(1)],
mandatory: true
},
[WorkflowProps.Routines]: {
validators: [FUNC]
}
};
class Settings {
constructor(settings, devSettings, instanceIndex) {
this.parseInput(settings, SETTINGS);
this.parseInput(devSettings, DEV_SETTINGS);
this.instanceIndex = instanceIndex;
this.initializeDelay = this.getInitializeDelay();
this.viewport = this.getViewport();
// todo: min/max indexes must be ignored if infinite mode is enabled ??
}
parseInput(input, props) {
const result = validate(input, props);
if (!result.isValid) {
throw new Error('Invalid settings');
}
Object.entries(result.params).forEach(([key, par]) => Object.assign(this, { [key]: par.value }));
}
getInitializeDelay() {
let result = 0;
if (this.windowViewport && this.initWindowDelay && !('scrollRestoration' in history)) {
result = this.initWindowDelay;
}
if (this.initDelay > 0) {
result = Math.max(result, this.initDelay);
}
return result;
}
getViewport() {
if (typeof this.viewportElement !== 'function') {
return this.viewportElement;
}
const value = this.viewportElement();
const result = validateOne({ value }, 'value', { validators: [VALIDATORS.ELEMENT] });
if (!result.isValid) {
return null; // fallback to default (null) if Function didn't return HTML element synchronously
}
return result.value;
}
}
const BaseProcessFactory = (process) => { var _a; return _a = class BaseProcess {
},
_a.process = process,
_a; };
const BaseAdapterProcessFactory = (process) => { var _a; return _a = class BaseAdapterProcess extends BaseProcessFactory(process) {
static parseInput(scroller, options, ignoreErrors = false, _process) {
const result = {
data: validate(options, ADAPTER_METHODS[_process || process])
};
if (result.data.isValid) {
result.params = Object.entries(result.data.params).reduce((acc, [key, { value }]) => (Object.assign(Object.assign({}, acc), { [key]: value })), {});
}
else {
scroller.logger.log(() => result.data.showErrors());
if (!ignoreErrors) {
scroller.workflow.call({
process,
status: ProcessStatus.error,
payload: { error: `Wrong argument of the "${process}" method call` }
});
}
}
return result;
}
},
_a.process = process,
_a; };
const initProcesses = [CommonProcess.init, AdapterProcess.reset, AdapterProcess.reload];
class Init extends BaseProcessFactory(CommonProcess.init) {
static run(scroller, process) {
const { state, workflow } = scroller;
const isInitial = initProcesses.includes(process);
scroller.logger.logCycle(true);
state.startWorkflowCycle(isInitial, process);
workflow.call({
process: Init.process,
status: ProcessStatus.next
});
}
}
class Scroll extends BaseProcessFactory(CommonProcess.scroll) {
static run(scroller, _payload) {
const { workflow, viewport } = scroller;
const position = viewport.scrollPosition;
if (Scroll.onSynthetic(scroller, position)) {
return;
}
Scroll.onThrottle(scroller, position, () => Scroll.onScroll(scroller, workflow));
}
static onSynthetic(scroller, position) {
const { scroll } = scroller.state;
const synthPos = scroll.syntheticPosition;
if (synthPos !== null) {
if (scroll.syntheticFulfill) {
scroll.syntheticPosition = null;
}
if (!scroll.syntheticFulfill || synthPos === position) {
scroller.logger.log(() => [
'skipping scroll',
position,
`[${scroll.syntheticFulfill ? '' : 'pre-'}synthetic]`
]);
return true;
}
scroller.logger.log(() => [
'synthetic scroll has been fulfilled:',
position,
position < synthPos ? '<' : '>',
synthPos
]);
}
return false;
}
static onThrottle(scroller, position, done) {
const { state, settings, logger } = scroller;
const { scroll } = state;
scroll.current = Scroll.getScrollEvent(position, scroll.previous);
const { direction, time } = scroll.current;
const timeDiff = scroll.previous ? time - scroll.previous.time : Infinity;
const delta = settings.throttle - timeDiff;
const shouldDelay = isFinite(delta) && delta > 0;
const alreadyDelayed = !!scroll.scrollTimer;
logger.log(() => [
direction === Direction.backward ? '\u2934' : '\u2935',
position,
shouldDelay ? timeDiff + 'ms' : '0ms',
shouldDelay ? (alreadyDelayed ? 'delayed' : `/ ${delta}ms delay`) : ''
]);
if (!shouldDelay) {
if (scroll.scrollTimer) {
clearTimeout(scroll.scrollTimer);
scroll.scrollTimer = null;
}
done();
return;
}
if (!alreadyDelayed) {
scroll.scrollTimer = setTimeout(() => {
logger.log(() => {
const curr = Scroll.getScrollEvent(scroller.viewport.scrollPosition, scroll.current);
return [
curr.direction === Direction.backward ? '\u2934' : '\u2935',
curr.position,
curr.time - time + 'ms',
'triggered by timer set on',
position
];
});
scroll.scrollTimer = null;
done();
}, delta);
}
}
static getScrollEvent(position, previous) {
const time = Number(new Date());
let direction = Direction.forward;
if (previous) {
if (position === previous.position) {
direction = previous.direction;
}
else if (position < previous.position) {
direction = Direction.backward;
}
}
return { position, direction, time };
}
static onScroll(scroller, workflow) {
const { scroll, cycle } = scroller.state;
scroll.previous = Object.assign({}, scroll.current);
scroll.current = null;
if (cycle.busy.get()) {
scroller.logger.log(() => [
'skipping scroll',
scroll.previous.position,
'[pending]'
]);
return;
}
workflow.call({
process: Scroll.process,
status: ProcessStatus.next
});
}
}
class Reset extends BaseAdapterProcessFactory(AdapterProcess.reset) {
static run(scroller, options) {
const { datasource, buffer, viewport, state } = scroller;
if (options) {
const { data } = Reset.parseInput(scroller, options);
if (!data.isValid) {
return;
}
const constructed = options instanceof Datasource;
Object.keys(DatasourceProps).forEach(key => {
const param = data.params[key];
const ds = datasource;
if (param.isSet || (constructed && ds[key])) {
ds[key] = param.value;
}
});
}
buffer.reset(true);
viewport.paddings.backward.reset();
viewport.paddings.forward.reset();
const payload = { datasource };
if (state.cycle.busy.get()) {
payload.finalize = true;
state.cycle.interrupter = Reset.process;
}
scroller.workflow.call({
process: Reset.process,
status: ProcessStatus.next,
payload
});
}
}
class Reload extends BaseAdapterProcessFactory(AdapterProcess.reload) {
static run(scroller, reloadIndex) {
const { viewport, state, buffer } = scroller;
const { params } = Reload.parseInput(scroller, { reloadIndex }, true);
buffer.reset(false, params ? params.reloadIndex : void 0);
viewport.reset(buffer.startIndex);
const payload = {};
if (state.cycle.busy.get()) {
payload.finalize = true;
state.cycle.interrupter = Reload.process;
}
scroller.workflow.call({
process: Reload.process,
status: ProcessStatus.next,
payload
});
}
}
class Item {
get $index() {
return this.container.$index;
}
set $index(value) {
this.container.$index = value;
}
get data() {
return this.container.data;
}
set data(value) {
this.container.data = value;
}
get element() {
return this.container.element;
}
set element(value) {
this.container.element = value;
}
constructor($index, data, routines) {
this.container = {
$index,
data
};
this.nodeId = String($index);
this.routines = routines;
this.invisible = true;
this.toRemove = false;
this.toInsert = false;
}
dispose() {
delete this.container.element;
}
setSize(preSize = 0) {
this.preSize = preSize;
if (this.element) {
this.size = this.routines.getSize(this.element);
}
}
makeVisible() {
this.routines.makeElementVisible(this.element);
this.invisible = false;
}
hide() {
if (this.element) {
this.routines.hideElement(this.element);
}
}
scrollTo(argument) {
if (this.element) {
this.routines.scrollTo(this.element, argument);
}
}
updateIndex(index) {
this.$index = index;
this.nodeId = String(index);
}
get() {
return this.container;
}
}
class Update extends BaseAdapterProcessFactory(AdapterProcess.update) {
static run(scroller, options) {
const { params } = Update.parseInput(scroller, options);
if (!params) {
return;
}
const shouldUpdate = Update.doUpdate(scroller, params);
scroller.workflow.call({
process: Update.process,
status: shouldUpdate ? ProcessStatus.next : ProcessStatus.done
});
}
static doUpdate(scroller, params) {
const { buffer, viewport, state, routines, logger } = scroller;
if (!buffer.items) {
logger.log(() => 'no items in Buffer');
return false;
}
const { item: firstItem, index: firstIndex, diff: firstItemDiff } = viewport.getEdgeVisibleItem(buffer.items, Direction.backward);
const { trackedIndex, toRemove } = buffer.updateItems(params.predicate, (index, data) => new Item(index, data, routines), firstIndex, !!params.fixRight);
let delta = 0;
const trackedItem = buffer.get(trackedIndex);
if (firstItem && firstItem === trackedItem) {
delta = -buffer.getSizeByIndex(trackedIndex) + firstItemDiff;
}
toRemove.forEach(item => item.hide());
logger.log(() => toRemove.length
? 'items to remove: [' + toRemove.map(({ $index }) => $index).join(',') + ']'
: 'no items to remove');
if (toRemove.length) {
// insertions will be processed on render
buffer.checkDefaultSize();
}
const toRender = buffer.items.filter(({ toInsert }) => toInsert);
logger.log(() => toRender.length
? 'items to render: [' + toRender.map(({ $index }) => $index).join(',') + ']'
: 'no items to render');
state.fetch.update(trackedIndex, delta, toRender, toRemove);
return !!toRemove.length || !!toRender.length;
}
}
class Insert extends BaseAdapterProcessFactory(AdapterProcess.insert) {
static run(scroller, options) {
const { params } = Insert.parseInput(scroller, options);
if (!params) {
return;
}
const shouldInsert = Insert.doInsert(scroller, params);
scroller.workflow.call({
process: Insert.process,
status: shouldInsert ? ProcessStatus.next : ProcessStatus.done
});
}
static doInsert(scroller, params) {
if (!Insert.insertEmpty(scroller, params)) {