Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ The viewport is the scrollable area whose position and size the scroller tracks;
| Setting | Type | Default | Effect |
| --- | --- | --- | --- |
| `windowViewport` | `boolean` | `false` | Use the browser window for scrolling and viewport dimensions. |
| `viewportElement` | `HTMLElement`, synchronous function returning one, or `null` | `null` | Use an explicit viewport instead of the content element's parent. Experimental. |
| `viewportElement` | `HTMLElement`, `null`, or a synchronous function returning either | `null` | Use an explicit viewport instead of the content element's parent. Experimental. |
| `horizontal` | `boolean` | `false` | Scroll and measure along the horizontal axis; the DOM/CSS layout must also be horizontal. |
| `alignShortContentToEnd` | `boolean` | `false` | Align content shorter than the viewport to the bottom/right. |

`windowViewport` takes precedence over `viewportElement`. A viewport factory is evaluated when the scroller is created; it must return an element synchronously. An invalid result falls back to the parent viewport.
`windowViewport` takes precedence over `viewportElement`. A viewport factory is evaluated when the scroller is created; it must return an element or `null` synchronously. A `null` or invalid result falls back to the parent viewport.

With `alignShortContentToEnd: true`, content shorter than the viewport is aligned to the bottom in vertical mode or to the right in horizontal mode; `false` aligns it to the top or left. For content that fills or exceeds the viewport, scrolling and the initial position selected by `startIndex` are unchanged. The setting neither reverses item order nor automatically keeps the viewport at the end when items are added.

Expand Down
2 changes: 1 addition & 1 deletion src/classes/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ export class Adapter<Item = unknown> implements IAdapter<Item> {
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
reset(options?: IDatasourceOptional): any {
reset(options?: IDatasourceOptional<Item>): any {
this.reloadCounter++;
this.logger.logAdapterMethod('reset', options, ` of ${this.reloadId}`);
this.workflow.call({
Expand Down
6 changes: 3 additions & 3 deletions src/classes/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export class Item<Data = unknown> implements _Item<Data> {
this.container.data = value;
}

get element(): HTMLElement {
return this.container.element as HTMLElement;
get element(): HTMLElement | undefined {
return this.container.element;
}
set element(value: HTMLElement) {
this.container.element = value;
Expand Down Expand Up @@ -68,7 +68,7 @@ export class Item<Data = unknown> implements _Item<Data> {
}

makeVisible(): void {
this.routines.makeElementVisible(this.element);
this.routines.makeElementVisible(this.element as HTMLElement);
this.invisible = false;
}

Expand Down
2 changes: 1 addition & 1 deletion src/classes/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class Settings<Data = unknown> implements ISettings, IDevSettings {
infinite: boolean;
horizontal: boolean;
windowViewport: boolean;
viewportElement: HTMLElement | (() => void) | null;
viewportElement: HTMLElement | (() => HTMLElement | null) | null;
alignShortContentToEnd: boolean; // align content shorter than the viewport to the bottom/right
onBeforeClip: ItemsProcessor | null; // if set, it will be run before clipping items from Buffer after they are hidden
sizeStrategy: SizeStrategy; // "average" | "frequent", determines behavior of unknown items
Expand Down
2 changes: 1 addition & 1 deletion src/classes/viewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class Viewport {
bwd ? i <= items.length - 1 : i >= 0;
i += bwd ? 1 : -1
) {
const itemEdge = this.routines.getEdge(items[i].element, opposite);
const itemEdge = this.routines.getEdge(items[i].element as HTMLElement, opposite);
diff = itemEdge - viewportEdge;
if ((bwd && diff > 0) || (!bwd && diff < 0)) {
item = items[i];
Expand Down
3 changes: 1 addition & 2 deletions src/interfaces/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ export interface AdapterInsertOptions<Data = unknown> {
beforeIndex?: number;
afterIndex?: number;
decrease?: boolean;
virtualize?: boolean;
}

export interface AdapterReplaceOptions<Data = unknown> {
Expand Down Expand Up @@ -164,7 +163,7 @@ export interface IAdapter<Data = unknown> {
readonly eof$: Reactive<boolean>;
readonly paused: boolean;
readonly paused$: Reactive<boolean>;
reset(datasource?: IDatasourceOptional): MethodResult;
reset(datasource?: IDatasourceOptional<Data>): MethodResult;
reload(reloadIndex?: number | string): MethodResult;
append(options: AdapterAppendOptions<Data>): MethodResult;
append(items: Data | Data[], eof?: boolean): MethodResult; // old signature
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type DSGetCallback<T> = (
success: SuccessFn<T>,
fail?: ErrorFn
) => void;
type DSGetObservable<T> = (index: number, count: number) => ObservableLike<T[]>;
type DSGetObservable<T> = (index: number, count: number) => ObservableLike<T>;
type DSGetPromise<T> = (index: number, count: number) => PromiseLike<T[]>;

export type DatasourceGet<T> = DSGetCallback<T> | DSGetObservable<T> | DSGetPromise<T>;
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ItemAdapter } from './adapter';
export interface Item<Data = unknown> {
uid: number;
$index: number;
element: HTMLElement;
element?: HTMLElement;
data: Data;
invisible: boolean;
get: () => ItemAdapter<Data>;
Expand Down
6 changes: 3 additions & 3 deletions src/interfaces/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ export interface Settings<T = unknown> {
windowViewport?: boolean;

/**
* Explicit viewport element, or a synchronous factory returning one; windowViewport takes precedence.
* An invalid factory result falls back to the content element's parent.
* Explicit viewport element, or a synchronous factory returning an element or null; windowViewport takes precedence.
* A null or invalid factory result falls back to the content element's parent.
* Experimental. Default: null (the content element's parent).
*/
viewportElement?: HTMLElement | (() => void) | null;
viewportElement?: HTMLElement | (() => HTMLElement | null) | null;

/**
* Aligns content shorter than the viewport to the bottom vertically or to the right horizontally.
Expand Down
8 changes: 6 additions & 2 deletions src/processes/adapter/insert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { Item } from '../../classes/item';
import { Direction } from '../../inputs/index';
import { AdapterInsertOptions, AdapterUpdateOptions } from '../../interfaces/index';

interface InsertParams extends AdapterInsertOptions {
virtualize?: boolean;
}

export default class Insert extends BaseAdapterProcessFactory(AdapterProcess.insert) {
static run(scroller: Scroller, options: AdapterInsertOptions): void {
const { params } = Insert.parseInput(scroller, options);
Expand All @@ -19,7 +23,7 @@ export default class Insert extends BaseAdapterProcessFactory(AdapterProcess.ins
});
}

static doInsert(scroller: Scroller, params: AdapterInsertOptions): boolean {
static doInsert(scroller: Scroller, params: InsertParams): boolean {
if (!Insert.insertEmpty(scroller, params)) {
if (!Insert.insertInBuffer(scroller, params)) {
if (!Insert.insertVirtually(scroller, params)) {
Expand Down Expand Up @@ -52,7 +56,7 @@ export default class Insert extends BaseAdapterProcessFactory(AdapterProcess.ins
return true;
}

static insertInBuffer(scroller: Scroller, params: AdapterInsertOptions): boolean {
static insertInBuffer(scroller: Scroller, params: InsertParams): boolean {
const { before, after, beforeIndex, afterIndex, items, decrease } = params;
const indexToInsert = scroller.buffer.getIndexToInsert(
before || after,
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/scaffolding/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ export const makeItems = (firstId: number, count: number): TestItem[] =>
export const starGet = ((
index: number,
count: number,
success: (items: unknown[]) => void
success: (items: TestItem[]) => void
): void => {
success(makeItems(index, count).map(item => ({ ...item, text: `${item.text} *` })));
}) as DatasourceGet<unknown>;
}) as DatasourceGet<TestItem>;

const minSize = 1;
const maxSize = 100;
Expand Down
12 changes: 5 additions & 7 deletions tests/e2e/specs/adapter.reset-persistence.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import {
makeTest,
Misc,
starGet,
DatasourceGet,
IDatasourceOptional,
TestConfig
TestConfig,
TestItem
} from '../scaffolding';

interface ResetVariant {
isNew: boolean;
options: IDatasourceOptional;
options: IDatasourceOptional<TestItem>;
}

type PermanentProp = Adapter.id | Adapter.version;
Expand Down Expand Up @@ -63,10 +63,8 @@ const reset = (misc: Misc, variant: ResetVariant) => {
return misc.adapter.reset(variant.options);
}

const datasource = new Datasource<unknown>({
get:
variant.options.get ??
(misc.datasource.get as unknown as DatasourceGet<unknown>),
const datasource = new Datasource<TestItem>({
get: variant.options.get ?? misc.datasource.get,
settings: variant.options.settings,
devSettings: variant.options.devSettings
});
Expand Down
11 changes: 6 additions & 5 deletions tests/e2e/specs/adapter.reset.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
Routines,
starGet,
IDatasourceOptional,
TestConfig
TestConfig,
TestItem
} from '../scaffolding';

interface ResetSnapshot {
Expand All @@ -20,7 +21,7 @@ interface ResetSnapshot {

interface ResetScenario {
config: TestConfig;
options?: IDatasourceOptional;
options?: IDatasourceOptional<TestItem>;
scrolls?: number;
direction?: Direction;
interruption?: boolean;
Expand Down Expand Up @@ -62,7 +63,7 @@ const settingsScenarios: ResetScenario[] = [baseConfigs[0], baseConfigs[2]].map(
settings: {
...config.datasourceSettings,
startIndex: 999
} as IDatasourceOptional['settings']
} as IDatasourceOptional<TestItem>['settings']
}
})
);
Expand Down Expand Up @@ -172,7 +173,7 @@ const expectReset = (
misc.expect.uniqueItems();
};

const reset = (misc: Misc, options?: IDatasourceOptional) =>
const reset = (misc: Misc, options?: IDatasourceOptional<TestItem>) =>
options ? misc.adapter.reset(options) : misc.adapter.reset();

const expectScrollListeners = (
Expand Down Expand Up @@ -358,7 +359,7 @@ describe('Adapter Reset Spec', () => {
const before = takeSnapshot(misc);

const result = await misc.adapter.reset(
options as IDatasourceOptional
options as IDatasourceOptional<TestItem>
);

expect(result.success).toBe(false);
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/settings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ describe('Settings', () => {
expect(calls).toBe(1);
});

it('falls back to null for invalid or asynchronous factory results', () => {
it('falls back to null when a factory does not return an element', () => {
[
undefined,
null,
Expand Down
Loading