Skip to content

Commit

Permalink
feat: add getItemContext method to DataProviderController (#6613)
Browse files Browse the repository at this point in the history
  • Loading branch information
vursen authored Oct 9, 2023
1 parent c785703 commit d902f9b
Show file tree
Hide file tree
Showing 8 changed files with 333 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
import type { ReactiveController } from 'lit';
import type { Cache } from './cache.js';
import type { getFlatIndexByPath, getFlatIndexContext, getItemContext } from './helpers.js';

type DataProviderDefaultParams = {
page: number;
Expand Down Expand Up @@ -57,6 +58,12 @@ export class DataProviderController<TItem, TDataProviderParams extends Record<st
*/
isExpanded: (item: TItem) => boolean;

/**
* A callback that returns the id for the given item and that
* is used when checking object items for equality.
*/
getItemId: (item: TItem) => unknown;

/**
* A reference to the root cache instance.
*/
Expand All @@ -67,6 +74,7 @@ export class DataProviderController<TItem, TDataProviderParams extends Record<st
config: {
size?: number;
pageSize: number;
getItemId(item: TItem): unknown;
isExpanded(item: TItem): boolean;
dataProvider: DataProvider<TItem, TDataProviderParams>;
dataProviderParams(): TDataProviderParams;
Expand Down Expand Up @@ -115,25 +123,33 @@ export class DataProviderController<TItem, TDataProviderParams extends Record<st
/**
* Returns context for the given flattened index, including:
* - the corresponding cache
* - the associated item (if loaded)
* - the corresponding index in the cache's items array.
* - the page containing the index.
* - the cache level
* - the corresponding item (if loaded)
* - the item's index in the cache's items array
* - the page containing the item
*/
getFlatIndexContext(flatIndex: number): ReturnType<typeof getFlatIndexContext<TItem>>;

/**
* Returns context for the given item, including:
* - the cache containing the item
* - the cache level
* - the item
* - the item's index in the cache's items array
* - the item's flattened index
* - the item's sub-cache (if exists)
* - the page containing the item
*
* If the item isn't found, the method returns undefined.
*/
getFlatIndexContext(flatIndex: number): {
cache: Cache<TItem>;
item: TItem | undefined;
index: number;
page: number;
level: number;
};
getItemContext(item: TItem): ReturnType<typeof getItemContext<TItem>>;

/**
* Returns the flattened index for the item that the given indexes point to.
* Each index in the path array points to a sub-item of the previous index.
* Using `Infinity` as an index will point to the last item on the level.
*/
getFlatIndexByPath(path: number[]): number;
getFlatIndexByPath(path: number[]): ReturnType<typeof getFlatIndexByPath<TItem>>;

/**
* Requests the data provider to load the page with the item corresponding
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { Cache } from './cache.js';
import { getFlatIndexByPath, getFlatIndexContext } from './helpers.js';
import { getFlatIndexByPath, getFlatIndexContext, getItemContext } from './helpers.js';

/**
* A controller that stores and manages items loaded with a data provider.
Expand Down Expand Up @@ -50,18 +50,27 @@ export class DataProviderController extends EventTarget {
*/
isExpanded;

/**
* A callback that returns the id for the given item and that
* is used when checking object items for equality.
*
* @type { (item: unknown) => unknown}
*/
getItemId;

/**
* A reference to the root cache instance.
*
* @param {Cache}
*/
rootCache;

constructor(host, { size, pageSize, isExpanded, dataProvider, dataProviderParams }) {
constructor(host, { size, pageSize, isExpanded, getItemId, dataProvider, dataProviderParams }) {
super();
this.host = host;
this.size = size;
this.pageSize = pageSize;
this.getItemId = getItemId;
this.isExpanded = isExpanded;
this.dataProvider = dataProvider;
this.dataProviderParams = dataProviderParams;
Expand Down Expand Up @@ -141,15 +150,33 @@ export class DataProviderController extends EventTarget {
/**
* Returns context for the given flattened index, including:
* - the corresponding cache
* - the associated item (if loaded)
* - the corresponding index in the cache's items array.
* - the page containing the index.
* - the cache level
* - the corresponding item (if loaded)
* - the item's index in the cache's items array
* - the page containing the item
*
* @param {number} flatIndex
*/
getFlatIndexContext(flatIndex) {
return getFlatIndexContext(this.rootCache, flatIndex);
}

/**
* Returns context for the given item, including:
* - the cache containing the item
* - the cache level
* - the item
* - the item's index in the cache's items array
* - the item's flattened index
* - the item's sub-cache (if exists)
* - the page containing the item
*
* If the item isn't found, the method returns undefined.
*/
getItemContext(item) {
return getItemContext({ getItemId: this.getItemId }, this.rootCache, item);
}

/**
* Returns the flattened index for the item that the given indexes point to.
* Each index in the path array points to a sub-item of the previous index.
Expand Down
47 changes: 39 additions & 8 deletions packages/component-base/src/data-provider-controller/helpers.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,61 @@
* Copyright (c) 2021 - 2023 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import type { Cache } from './cache.js';

/**
* Returns context for the given flattened index, including:
* - the corresponding cache
* - the associated item (if loaded)
* - the corresponding index in the cache's items array.
* - the page containing the index.
* - the cache level
* - the corresponding item (if loaded)
* - the item's index in the cache's items array
* - the page containing the item
*/
export function getFlatIndexContext(
cache: Cache,
export function getFlatIndexContext<TItem>(
cache: Cache<TItem>,
flatIndex: number,
level: number,
): {
cache: Cache;
item: unknown | undefined;
cache: Cache<TItem>;
item: TItem | undefined;
index: number;
page: number;
level: number;
};

/**
* Returns context for the given item, including:
* - the cache containing the item
* - the cache level
* - the item
* - the item's index in the cache's items array
* - the item's flattened index
* - the item's sub-cache (if exists)
* - the page containing the item
*
* If the item isn't found, the method returns undefined.
*/
export function getItemContext<TItem>(
context: { getItemId(item: TItem): unknown },
cache: Cache<TItem>,
targetItem: TItem,
level: number,
levelFlatIndex: number,
):
| {
level: number;
item: TItem;
index: number;
page: number;
flatIndex: number;
cache: Cache<TItem>;
subCache: Cache<TItem> | undefined;
}
| undefined;

/**
* Recursively returns the globally flat index of the item the given indexes point to.
* Each index in the array points to a sub-item of the previous index.
* Using `Infinity` as an index will point to the last item on the level.
*/
export function getFlatIndexByPath(cache: Cache, path: number[], flatIndex: number): number;
export function getFlatIndexByPath<TItem>(cache: Cache<TItem>, path: number[], flatIndex: number): number;
59 changes: 54 additions & 5 deletions packages/component-base/src/data-provider-controller/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/

/**
* @typedef {import('./cache.js').Cache} Cache
*/

/**
* Returns context for the given flattened index, including:
* - the corresponding cache
* - the associated item (if loaded)
* - the corresponding index in the cache's items array.
* - the page containing the index.
* - the cache level
* - the corresponding item (if loaded)
* - the item's index in the cache's items array
* - the page containing the item
*
* @param {import('./cache.js').Cache} cache
* @param {Cache} cache
* @param {number} flatIndex
* @return {{ cache: Cache, item: object | undefined, index: number, page: number, level: number }}
*/
export function getFlatIndexContext(cache, flatIndex, level = 0) {
let levelIndex = flatIndex;
Expand All @@ -38,6 +41,52 @@ export function getFlatIndexContext(cache, flatIndex, level = 0) {
};
}

/**
* Returns context for the given item, including:
* - the cache containing the item
* - the cache level
* - the item
* - the item's index in the cache's items array
* - the item's flattened index
* - the item's sub-cache (if exists)
* - the page containing the item
*
* If the item isn't found, the method returns undefined.
*
* @param {Cache} cache
* @param {{ getItemId: (item: unknown) => unknown}} context
* @param {Cache} cache
* @param {unknown} targetItem
* @param {number} level
* @param {number} levelFlatIndex
*/
export function getItemContext({ getItemId }, cache, targetItem, level = 0, levelFlatIndex = 0) {
// Start looking in this cache
for (let index = 0; index < cache.items.length; index++) {
const item = cache.items[index];
if (!!item && getItemId(item) === getItemId(targetItem)) {
return {
cache,
level,
item,
index,
page: Math.floor(index / cache.pageSize),
subCache: cache.getSubCache(index),
flatIndex: levelFlatIndex + cache.getFlatIndex(index),
};
}
}

// Look through sub-caches
for (const subCache of cache.subCaches) {
const parentItemFlatIndex = levelFlatIndex + cache.getFlatIndex(subCache.parentCacheIndex);
const result = getItemContext({ getItemId }, subCache, targetItem, level + 1, parentItemFlatIndex + 1);
if (result) {
return result;
}
}
}

/**
* Recursively returns the globally flat index of the item the given indexes point to.
* Each index in the array points to a sub-item of the previous index.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export function createDataProvider({ size, async }) {
export function createDataProvider({ size, async, generator }) {
generator ||= (parentItem, i) => `${parentItem ?? 'Item'}-${i}`;

return ({ page, pageSize, parentItem }, callback) => {
const items = Array.from({ length: size }, (_, i) => `${parentItem ?? 'Item'}-${i}`);
const items = Array.from({ length: size }, (_, i) => generator(parentItem, i));
const result = items.splice(page * pageSize, pageSize);

if (async) {
Expand Down
Loading

0 comments on commit d902f9b

Please sign in to comment.