diff --git a/package.json b/package.json index 2e115d19..c0371418 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "dependencies": { "lodash.debounce": "4.0.8", "prop-types": "15.5.8", - "recyclerlistview-gridlayoutprovider": "1.0.1", "ts-object-utils": "0.0.5" }, "peerDependencies": { diff --git a/src/core/dependencies/GridLayoutProvider.ts b/src/core/dependencies/GridLayoutProvider.ts new file mode 100644 index 00000000..238d698c --- /dev/null +++ b/src/core/dependencies/GridLayoutProvider.ts @@ -0,0 +1,57 @@ +import { LayoutProvider, Dimension } from "./LayoutProvider"; +import { Layout, LayoutManager } from "../layoutmanager/LayoutManager"; +import { GridLayoutManager } from "../layoutmanager/GridLayoutManager"; + +export class GridLayoutProvider extends LayoutProvider { + private _getHeightOrWidth: (index: number) => number; + private _getSpan: (index: number) => number; + private _maxSpan: number; + private _renderWindowSize?: Dimension; + private _isHorizontal?: boolean; + private _acceptableRelayoutDelta: number; + constructor( + maxSpan: number, + getLayoutType: (index: number) => string | number, + getSpan: (index: number) => number, + // If horizonal return width while spans will be rowspans. Opposite holds true if not horizontal + getHeightOrWidth: (index: number) => number, + acceptableRelayoutDelta?: number, + ) { + super( + getLayoutType, + (type: string | number, dimension: Dimension, index: number) => { + this.setLayout(dimension, index); + }, + ); + this._getHeightOrWidth = getHeightOrWidth; + this._getSpan = getSpan; + this._maxSpan = maxSpan; + this._acceptableRelayoutDelta = ((acceptableRelayoutDelta === undefined) || (acceptableRelayoutDelta === null)) ? 1 : acceptableRelayoutDelta; + } + + public newLayoutManager(renderWindowSize: Dimension, isHorizontal?: boolean, cachedLayouts?: Layout[]): LayoutManager { + this._isHorizontal = isHorizontal; + this._renderWindowSize = renderWindowSize; + return new GridLayoutManager(this, renderWindowSize, this._getSpan, this._maxSpan, this._acceptableRelayoutDelta, this._isHorizontal, cachedLayouts); + } + + private setLayout(dimension: Dimension, index: number): void { + const maxSpan: number = this._maxSpan; + const itemSpan: number = this._getSpan(index); + if (itemSpan > maxSpan) { + throw new Error("Item span for index " + index + " is more than the max span"); + } + if (this._renderWindowSize) { + if (this._isHorizontal) { + dimension.width = this._getHeightOrWidth(index); + dimension.height = (this._renderWindowSize.height / maxSpan) * itemSpan; + + } else { + dimension.height = this._getHeightOrWidth(index); + dimension.width = (this._renderWindowSize.width / maxSpan) * itemSpan; + } + } else { + throw new Error("setLayout called before layoutmanager was created, cannot be handled"); + } + } +} diff --git a/src/core/layoutmanager/GridLayoutManager.ts b/src/core/layoutmanager/GridLayoutManager.ts new file mode 100644 index 00000000..9dd2d45d --- /dev/null +++ b/src/core/layoutmanager/GridLayoutManager.ts @@ -0,0 +1,77 @@ +import { LayoutProvider } from "./../dependencies/LayoutProvider"; +import { WrapGridLayoutManager, Layout } from "./LayoutManager"; +import { Dimension } from "../dependencies/LayoutProvider"; + +export class GridLayoutManager extends WrapGridLayoutManager { + private _maxSpan: number; + private _getSpan: (index: number) => number; + private _isGridHorizontal: boolean | undefined; + private _renderWindowSize: Dimension; + private _acceptableRelayoutDelta: number; + constructor( + layoutProvider: LayoutProvider, + renderWindowSize: Dimension, + getSpan: (index: number) => number, + maxSpan: number, + acceptableRelayoutDelta: number, + isHorizontal?: boolean, + cachedLayouts?: Layout[], + ) { + super(layoutProvider, renderWindowSize, isHorizontal, cachedLayouts); + this._getSpan = getSpan; + this._isGridHorizontal = isHorizontal; + this._renderWindowSize = renderWindowSize; + if (acceptableRelayoutDelta < 0) { + throw new Error("acceptableRelayoutDelta cannot be less than 0"); + } else { + this._acceptableRelayoutDelta = acceptableRelayoutDelta; + } + if (maxSpan <= 0) { + throw new Error("Max Column Span cannot be less than or equal to 0"); + } else { + this._maxSpan = maxSpan; + } + } + + public overrideLayout(index: number, dim: Dimension): boolean { + // we are doing this because - when we provide decimal dimensions for a + // certain cell - the onlayout returns a different dimension in certain high end devices. + // This causes the layouting to behave weirdly as the new dimension might not adhere to the spans and the cells arrange themselves differently + // So, whenever we have layouts for a certain index, we explicitly override the dimension to those very layout values + // and call super so as to set the overridden flag as true + const layout = this.getLayouts()[index]; + const heightDiff = Math.abs(dim.height - layout.height); + const widthDiff = Math.abs(dim.width - layout.width); + if (layout) { + if (this._isGridHorizontal) { + if (heightDiff < this._acceptableRelayoutDelta) { + if (widthDiff === 0) { + return false; + } + dim.height = layout.height; + } + } else { + if (widthDiff < this._acceptableRelayoutDelta) { + if (heightDiff === 0) { + return false; + } + dim.width = layout.width; + } + } + } + return super.overrideLayout(index, dim); + } + + public getStyleOverridesForIndex(index: number): object | undefined { + const columnSpanForIndex = this._getSpan(index); + return this._isGridHorizontal + ? { + height: + (this._renderWindowSize.height / this._maxSpan) * columnSpanForIndex, + } + : { + width: + (this._renderWindowSize.width / this._maxSpan) * columnSpanForIndex, + }; + } + } diff --git a/src/index.ts b/src/index.ts index 9330cc8b..e1344c4e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,13 @@ import ContextProvider from "./core/dependencies/ContextProvider"; import DataProvider, { BaseDataProvider } from "./core/dependencies/DataProvider"; import { BaseLayoutProvider, Dimension, LayoutProvider } from "./core/dependencies/LayoutProvider"; -import { GridLayoutProvider, GridLayoutManager } from "recyclerlistview-gridlayoutprovider"; +import { GridLayoutProvider } from "./core/dependencies/GridLayoutProvider"; import RecyclerListView, { OnRecreateParams } from "./core/RecyclerListView"; import BaseScrollView from "./core/scrollcomponent/BaseScrollView"; import { BaseItemAnimator } from "./core/ItemAnimator"; import { AutoScroll } from "./utils/AutoScroll"; import { Layout, LayoutManager, Point, WrapGridLayoutManager } from "./core/layoutmanager/LayoutManager"; +import { GridLayoutManager } from "./core/layoutmanager/GridLayoutManager"; import ProgressiveListView from "./core/ProgressiveListView"; import { DebugHandlers } from "./core/devutils/debughandlers/DebugHandlers"; import { ComponentCompat } from "./utils/ComponentCompat";