diff --git a/BREAKING.md b/BREAKING.md
index 120d64795b..1e0a895a72 100644
--- a/BREAKING.md
+++ b/BREAKING.md
@@ -14,6 +14,7 @@ This is a comprehensive list of the breaking changes introduced in the major ver
- [Components](#version-7x-components)
- [Overlays](#version-7x-overlays)
+ - [Virtual Scroll](#version-7x-virtual-scroll)
Components
@@ -21,6 +22,18 @@ This is a comprehensive list of the breaking changes introduced in the major ver
Ionic now listens on the `keydown` event instead of the `keyup` event when determining when to dismiss overlays via the "Escape" key. Any applications that were listening on `keyup` to suppress this behavior should listen on `keydown` instead.
+
Virtual Scroll
+
+`ion-virtual-scroll` has been removed from Ionic.
+
+Developers using the component will need to migrate to a virtual scroll solution provided by their framework:
+
+- [Angular](https://ionicframework.com/docs/angular/virtual-scroll)
+- [React](https://ionicframework.com/docs/react/virtual-scroll)
+- [Vue](https://ionicframework.com/docs/vue/virtual-scroll)
+
+Any references to the virtual scroll types from `@ionic/core` have been removed. Please remove or replace these types: `Cell`, `VirtualNode`, `CellType`, `NodeChange`, `HeaderFn`, `ItemHeightFn`, `FooterHeightFn`, `ItemRenderFn` and `DomRenderFn`.
+
## Version 6.x
diff --git a/angular/.eslintignore b/angular/.eslintignore
index fab9337a6d..641ed17d70 100644
--- a/angular/.eslintignore
+++ b/angular/.eslintignore
@@ -1,4 +1,3 @@
dist
-virtual-scroll
scripts
proxies.ts
diff --git a/angular/.eslintrc.json b/angular/.eslintrc.json
index e89c2bfa7c..8b2e7077b4 100644
--- a/angular/.eslintrc.json
+++ b/angular/.eslintrc.json
@@ -1,6 +1,6 @@
{
"root": true,
- "ignorePatterns": ["test/**/*", "src/directives/virtual-scroll/**/*"],
+ "ignorePatterns": ["test/**/*"],
"overrides": [
{
"files": ["*.ts"],
diff --git a/angular/.prettierignore b/angular/.prettierignore
index 813f6109a4..146ebf4ddb 100644
--- a/angular/.prettierignore
+++ b/angular/.prettierignore
@@ -1,5 +1,4 @@
dist
-virtual-scroll
scripts
test
src/directives/proxies.ts
diff --git a/angular/src/directives/virtual-scroll/virtual-footer.ts b/angular/src/directives/virtual-scroll/virtual-footer.ts
deleted file mode 100644
index 3c42173754..0000000000
--- a/angular/src/directives/virtual-scroll/virtual-footer.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Directive, TemplateRef } from '@angular/core';
-
-import { VirtualContext } from './virtual-utils';
-
-/**
- * @hidden
- */
-@Directive({ selector: '[virtualFooter]' })
-export class VirtualFooter {
- constructor(public templateRef: TemplateRef) {}
-}
diff --git a/angular/src/directives/virtual-scroll/virtual-header.ts b/angular/src/directives/virtual-scroll/virtual-header.ts
deleted file mode 100644
index a4f519efe7..0000000000
--- a/angular/src/directives/virtual-scroll/virtual-header.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Directive, TemplateRef } from '@angular/core';
-
-import { VirtualContext } from './virtual-utils';
-
-/**
- * @hidden
- */
-@Directive({ selector: '[virtualHeader]' })
-export class VirtualHeader {
- constructor(public templateRef: TemplateRef) {}
-}
diff --git a/angular/src/directives/virtual-scroll/virtual-item.ts b/angular/src/directives/virtual-scroll/virtual-item.ts
deleted file mode 100644
index 93056d34ea..0000000000
--- a/angular/src/directives/virtual-scroll/virtual-item.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Directive, TemplateRef, ViewContainerRef } from '@angular/core';
-
-import { VirtualContext } from './virtual-utils';
-
-/**
- * @hidden
- */
-@Directive({ selector: '[virtualItem]' })
-export class VirtualItem {
- constructor(public templateRef: TemplateRef, public viewContainer: ViewContainerRef) {}
-}
diff --git a/angular/src/directives/virtual-scroll/virtual-scroll.ts b/angular/src/directives/virtual-scroll/virtual-scroll.ts
deleted file mode 100644
index 74695d85e7..0000000000
--- a/angular/src/directives/virtual-scroll/virtual-scroll.ts
+++ /dev/null
@@ -1,221 +0,0 @@
-import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, EmbeddedViewRef, IterableDiffer, IterableDiffers, NgZone, SimpleChanges, TrackByFunction } from '@angular/core';
-import { Cell, CellType, FooterHeightFn, HeaderFn, HeaderHeightFn, ItemHeightFn } from '@ionic/core';
-
-import { ProxyCmp } from '../angular-component-lib/utils';
-
-import { VirtualFooter } from './virtual-footer';
-import { VirtualHeader } from './virtual-header';
-import { VirtualItem } from './virtual-item';
-import { VirtualContext } from './virtual-utils';
-
-export declare interface IonVirtualScroll {
- /**
- * It is important to provide this
- * if virtual item height will be significantly larger than the default
- * The approximate height of each virtual item template's cell.
- * This dimension is used to help determine how many cells should
- * be created when initialized, and to help calculate the height of
- * the scrollable area. This height value can only use `px` units.
- * Note that the actual rendered size of each cell comes from the
- * app's CSS, whereas this approximation is used to help calculate
- * initial dimensions before the item has been rendered.
- */
- approxItemHeight: number;
-
- /**
- * The approximate height of each header template's cell.
- * This dimension is used to help determine how many cells should
- * be created when initialized, and to help calculate the height of
- * the scrollable area. This height value can only use `px` units.
- * Note that the actual rendered size of each cell comes from the
- * app's CSS, whereas this approximation is used to help calculate
- * initial dimensions before the item has been rendered.
- */
- approxHeaderHeight: number;
-
- /**
- * The approximate width of each footer template's cell.
- * This dimension is used to help determine how many cells should
- * be created when initialized, and to help calculate the height of
- * the scrollable area. This height value can only use `px` units.
- * Note that the actual rendered size of each cell comes from the
- * app's CSS, whereas this approximation is used to help calculate
- * initial dimensions before the item has been rendered.
- */
- approxFooterHeight: number;
-
- /**
- * Section headers and the data used within its given
- * template can be dynamically created by passing a function to `headerFn`.
- * For example, a large list of contacts usually has dividers between each
- * letter in the alphabet. App's can provide their own custom `headerFn`
- * which is called with each record within the dataset. The logic within
- * the header function can decide if the header template should be used,
- * and what data to give to the header template. The function must return
- * `null` if a header cell shouldn't be created.
- */
- headerFn?: HeaderFn;
-
- /**
- * Section footers and the data used within its given
- * template can be dynamically created by passing a function to `footerFn`.
- * The logic within the footer function can decide if the footer template
- * should be used, and what data to give to the footer template. The function
- * must return `null` if a footer cell shouldn't be created.
- */
- footerFn?: HeaderFn;
-
- /**
- * The data that builds the templates within the virtual scroll.
- * It's important to note that when this data has changed, then the
- * entire virtual scroll is reset, which is an expensive operation and
- * should be avoided if possible.
- */
- items?: any[] | null;
-
- /**
- * An optional function that maps each item within their height.
- * When this function is provided, heavy optimizations and fast path can be taked by
- * `ion-virtual-scroll` leading to massive performance improvements.
- *
- * This function allows to skip all DOM reads, which can be Doing so leads
- * to massive performance
- */
- itemHeight?: ItemHeightFn;
-
- /**
- * An optional function that maps each item header within their height.
- */
- headerHeight?: HeaderHeightFn;
-
- /**
- * An optional function that maps each item footer within their height.
- */
- footerHeight?: FooterHeightFn;
-
- /**
- * Same as `ngForTrackBy` which can be used on `ngFor`.
- */
- trackBy: TrackByFunction;
-
- /**
- * This method marks the tail the items array as dirty, so they can be re-rendered. It's equivalent to calling: ```js * virtualScroll.checkRange(lastItemLen, items.length - lastItemLen); * ```
- */
- 'checkEnd': () => void;
- /**
- * This method marks a subset of items as dirty, so they can be re-rendered. Items should be marked as dirty any time the content or their style changes. The subset of items to be updated can are specifing by an offset and a length.
- */
- 'checkRange': (offset: number, len?: number) => void;
- /**
- * Returns the position of the virtual item at the given index.
- */
- 'positionForItem': (index: number) => Promise;
-}
-
-@ProxyCmp({
- inputs: ['approxItemHeight', 'approxHeaderHeight', 'approxFooterHeight', 'headerFn', 'footerFn', 'items', 'itemHeight', 'headerHeight', 'footerHeight'],
- methods: ['checkEnd', 'checkRange', 'positionForItem']
-})
-@Component({
- selector: 'ion-virtual-scroll',
- template: '',
- changeDetection: ChangeDetectionStrategy.OnPush,
- inputs: [
- 'approxItemHeight',
- 'approxHeaderHeight',
- 'approxFooterHeight',
- 'headerFn',
- 'footerFn',
- 'items',
- 'itemHeight',
- 'headerHeight',
- 'footerHeight',
- 'trackBy'
- ]
-})
-export class IonVirtualScroll {
-
- private differ?: IterableDiffer;
- private el: HTMLIonVirtualScrollElement;
- private refMap = new WeakMap>();
-
- @ContentChild(VirtualItem, { static: false }) itmTmp!: VirtualItem;
- @ContentChild(VirtualHeader, { static: false }) hdrTmp!: VirtualHeader;
- @ContentChild(VirtualFooter, { static: false }) ftrTmp!: VirtualFooter;
-
- constructor(
- private z: NgZone,
- private iterableDiffers: IterableDiffers,
- elementRef: ElementRef,
- ) {
- this.el = elementRef.nativeElement as HTMLIonVirtualScrollElement;
- this.el.nodeRender = this.nodeRender.bind(this);
- }
-
- ngOnChanges(changes: SimpleChanges): void {
- if (this.trackBy && 'items' in changes) {
- // React on virtualScroll changes only once all inputs have been initialized
- const value = changes['items'].currentValue;
- if (this.differ === undefined && value != null) {
- try {
- this.differ = this.iterableDiffers.find(value).create(this.trackBy);
- } catch (e) {
- throw new Error(
- `Cannot find a differ supporting object '${value}'. VirtualScroll only supports binding to Iterables such as Arrays.`);
- }
- }
- }
- }
-
- ngDoCheck() {
- // and if there actually are changes
- const changes = this.differ !== undefined && this.items ? this.differ.diff(this.items) : null;
- if (changes === null) {
- return;
- }
- // TODO: optimize
- this.checkRange(0);
- }
-
- private nodeRender(el: HTMLElement | null, cell: Cell, index: number): HTMLElement {
- return this.z.run(() => {
- let node: EmbeddedViewRef;
- if (!el) {
- node = this.itmTmp.viewContainer.createEmbeddedView(
- this.getComponent(cell.type),
- { $implicit: cell.value, index },
- index
- );
- el = getElement(node);
- this.refMap.set(el, node);
- } else {
- node = this.refMap.get(el)!;
- const ctx = node.context;
- ctx.$implicit = cell.value;
- ctx.index = cell.index;
- }
- // run sync change detections
- node.detectChanges();
- return el;
- });
- }
-
- private getComponent(type: CellType) {
- switch (type) {
- case 'item': return this.itmTmp.templateRef;
- case 'header': return this.hdrTmp.templateRef;
- case 'footer': return this.ftrTmp.templateRef;
- default: throw new Error('template for virtual item was not provided');
- }
- }
-}
-
-const getElement = (view: EmbeddedViewRef): HTMLElement => {
- const rootNodes = view.rootNodes;
- for (let i = 0; i < rootNodes.length; i++) {
- if (rootNodes[i].nodeType === 1) {
- return rootNodes[i];
- }
- }
- throw new Error('virtual element was not created');
-};
diff --git a/angular/src/directives/virtual-scroll/virtual-utils.ts b/angular/src/directives/virtual-scroll/virtual-utils.ts
deleted file mode 100644
index fc6336844f..0000000000
--- a/angular/src/directives/virtual-scroll/virtual-utils.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-
-export interface VirtualContext {
- $implicit: any;
- index: number;
-}
diff --git a/angular/src/index.ts b/angular/src/index.ts
index a28a12dfef..11f09c79cb 100644
--- a/angular/src/index.ts
+++ b/angular/src/index.ts
@@ -14,10 +14,6 @@ export {
} from './directives/navigation/router-link-delegate';
export { NavParams } from './directives/navigation/nav-params';
-export { IonVirtualScroll } from './directives/virtual-scroll/virtual-scroll';
-export { VirtualItem } from './directives/virtual-scroll/virtual-item';
-export { VirtualHeader } from './directives/virtual-scroll/virtual-header';
-export { VirtualFooter } from './directives/virtual-scroll/virtual-footer';
export { IonModal } from './directives/overlays/modal';
export { IonPopover } from './directives/overlays/popover';
export * from './directives/proxies';
diff --git a/angular/src/ionic-module.ts b/angular/src/ionic-module.ts
index 9470fcf4c0..34afe3f08e 100644
--- a/angular/src/ionic-module.ts
+++ b/angular/src/ionic-module.ts
@@ -19,10 +19,6 @@ import {
import { IonModal } from './directives/overlays/modal';
import { IonPopover } from './directives/overlays/popover';
import { DIRECTIVES } from './directives/proxies-list';
-import { VirtualFooter } from './directives/virtual-scroll/virtual-footer';
-import { VirtualHeader } from './directives/virtual-scroll/virtual-header';
-import { VirtualItem } from './directives/virtual-scroll/virtual-item';
-import { IonVirtualScroll } from './directives/virtual-scroll/virtual-scroll';
import { AngularDelegate } from './providers/angular-delegate';
import { ConfigToken } from './providers/config';
import { ModalController } from './providers/modal-controller';
@@ -50,12 +46,6 @@ const DECLARATIONS = [
NavDelegate,
RouterLinkDelegateDirective,
RouterLinkWithHrefDelegateDirective,
-
- // virtual scroll
- VirtualFooter,
- VirtualHeader,
- VirtualItem,
- IonVirtualScroll,
];
@NgModule({
diff --git a/angular/test/base/e2e/src/virtual-scroll.spec.ts b/angular/test/base/e2e/src/virtual-scroll.spec.ts
deleted file mode 100644
index 5a562dd3a6..0000000000
--- a/angular/test/base/e2e/src/virtual-scroll.spec.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-describe('Virtual Scroll', () => {
- beforeEach(() => {
- cy.visit('/virtual-scroll');
- cy.wait(30);
- })
-
- it('should open virtual-scroll', () => {
- cy.get('ion-virtual-scroll > *').its('length').should('be.gt', 0);
- });
-});
-
diff --git a/angular/test/base/src/app/app-routing.module.ts b/angular/test/base/src/app/app-routing.module.ts
index f40f265118..195bbdbf47 100644
--- a/angular/test/base/src/app/app-routing.module.ts
+++ b/angular/test/base/src/app/app-routing.module.ts
@@ -7,8 +7,6 @@ import { RouterLinkPageComponent } from './router-link-page/router-link-page.com
import { RouterLinkPage2Component } from './router-link-page2/router-link-page2.component';
import { RouterLinkPage3Component } from './router-link-page3/router-link-page3.component';
import { HomePageComponent } from './home-page/home-page.component';
-import { VirtualScrollComponent } from './virtual-scroll/virtual-scroll.component';
-import { VirtualScrollDetailComponent } from './virtual-scroll-detail/virtual-scroll-detail.component';
import { NestedOutletComponent } from './nested-outlet/nested-outlet.component';
import { NestedOutletPageComponent } from './nested-outlet-page/nested-outlet-page.component';
import { NestedOutletPage2Component } from './nested-outlet-page2/nested-outlet-page2.component';
@@ -40,8 +38,6 @@ const routes: Routes = [
{ path: 'router-link-page2/:id', component: RouterLinkPage2Component },
{ path: 'router-link-page3', component: RouterLinkPage3Component },
{ path: 'slides', component: SlidesComponent },
- { path: 'virtual-scroll', component: VirtualScrollComponent },
- { path: 'virtual-scroll-detail/:itemId', component: VirtualScrollDetailComponent },
{ path: 'tabs', redirectTo: '/tabs/account', pathMatch: 'full' },
{
path: 'navigation',
diff --git a/angular/test/base/src/app/app.module.ts b/angular/test/base/src/app/app.module.ts
index 563932b73d..acd14de9aa 100644
--- a/angular/test/base/src/app/app.module.ts
+++ b/angular/test/base/src/app/app.module.ts
@@ -15,9 +15,6 @@ import { RouterLinkPageComponent } from './router-link-page/router-link-page.com
import { RouterLinkPage2Component } from './router-link-page2/router-link-page2.component';
import { RouterLinkPage3Component } from './router-link-page3/router-link-page3.component';
import { HomePageComponent } from './home-page/home-page.component';
-import { VirtualScrollComponent } from './virtual-scroll/virtual-scroll.component';
-import { VirtualScrollDetailComponent } from './virtual-scroll-detail/virtual-scroll-detail.component';
-import { VirtualScrollInnerComponent } from './virtual-scroll-inner/virtual-scroll-inner.component';
import { NestedOutletComponent } from './nested-outlet/nested-outlet.component';
import { NestedOutletPageComponent } from './nested-outlet-page/nested-outlet-page.component';
import { NestedOutletPage2Component } from './nested-outlet-page2/nested-outlet-page2.component';
@@ -44,9 +41,6 @@ import { AccordionModalComponent } from './accordion/accordion-modal/accordion-m
RouterLinkPage2Component,
RouterLinkPage3Component,
HomePageComponent,
- VirtualScrollComponent,
- VirtualScrollDetailComponent,
- VirtualScrollInnerComponent,
NestedOutletComponent,
NestedOutletPageComponent,
NestedOutletPage2Component,
diff --git a/angular/test/base/src/app/home-page/home-page.component.html b/angular/test/base/src/app/home-page/home-page.component.html
index 741812dc79..9fa61f4b7b 100644
--- a/angular/test/base/src/app/home-page/home-page.component.html
+++ b/angular/test/base/src/app/home-page/home-page.component.html
@@ -1,71 +1,66 @@
-
-
- Test App
-
-
-
-
-
-
-
- Alerts test
-
-
-
-
- Inputs test
-
-
-
-
- Form test
-
-
-
-
- Modals test
-
-
-
-
- Router link test
-
-
-
-
- Tabs test
-
-
-
-
- Slides test
-
-
-
-
- Virtual Scroll
-
-
-
-
- Nested ion-router-outlet
-
-
-
-
- ViewChild()
-
-
-
-
- Providers
-
-
-
-
- Accordions Test
-
-
-
-
+
+
+ Test App
+
+
+
+
+
+
+
+ Alerts test
+
+
+
+
+ Inputs test
+
+
+
+
+ Form test
+
+
+
+
+ Modals test
+
+
+
+
+ Router link test
+
+
+
+
+ Tabs test
+
+
+
+
+ Slides test
+
+
+
+
+ Nested ion-router-outlet
+
+
+
+
+ ViewChild()
+
+
+
+
+ Providers
+
+
+
+
+ Accordions Test
+
+
+
+
diff --git a/angular/test/base/src/app/virtual-scroll-detail/virtual-scroll-detail.component.html b/angular/test/base/src/app/virtual-scroll-detail/virtual-scroll-detail.component.html
deleted file mode 100644
index 02fc1e28db..0000000000
--- a/angular/test/base/src/app/virtual-scroll-detail/virtual-scroll-detail.component.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
- virtual-scroll page
-
-
-
-
-
diff --git a/angular/test/base/src/app/virtual-scroll-inner/virtual-scroll-inner.component.ts b/angular/test/base/src/app/virtual-scroll-inner/virtual-scroll-inner.component.ts
deleted file mode 100644
index 3bd7a4beaa..0000000000
--- a/angular/test/base/src/app/virtual-scroll-inner/virtual-scroll-inner.component.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Component, OnInit, NgZone, Input } from '@angular/core';
-
-@Component({
- selector: 'app-virtual-scroll-inner',
- templateUrl: './virtual-scroll-inner.component.html',
-})
-export class VirtualScrollInnerComponent implements OnInit {
-
- @Input() value: string;
- onInit = 0;
-
- ngOnInit() {
- NgZone.assertInAngularZone();
- this.onInit++;
- console.log('created');
- }
-}
diff --git a/angular/test/base/src/app/virtual-scroll/virtual-scroll.component.html b/angular/test/base/src/app/virtual-scroll/virtual-scroll.component.html
deleted file mode 100644
index 8caec59284..0000000000
--- a/angular/test/base/src/app/virtual-scroll/virtual-scroll.component.html
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
- Virtual Scroll Test
-
-
-
-
-
-
-
-
-
-
- {{ header }}
- -- {{ footer }}
-
-
-
-
-
-
-
-
-
-
diff --git a/angular/test/base/src/app/virtual-scroll/virtual-scroll.component.ts b/angular/test/base/src/app/virtual-scroll/virtual-scroll.component.ts
deleted file mode 100644
index 71d09d0a3f..0000000000
--- a/angular/test/base/src/app/virtual-scroll/virtual-scroll.component.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { Component, OnInit, ViewChild } from '@angular/core';
-import { HeaderFn } from '@ionic/core';
-import { IonVirtualScroll } from '@ionic/angular';
-
-@Component({
- selector: 'app-virtual-scroll',
- templateUrl: './virtual-scroll.component.html',
-})
-export class VirtualScrollComponent {
-
- @ViewChild(IonVirtualScroll, { static: true }) virtualScroll: IonVirtualScroll;
-
- items = Array.from({length: 100}, (_, i) => ({ name: `${i}`, checked: true}));
-
- itemHeight = () => 44;
-
- myHeaderFn: HeaderFn = (_, index) => {
- if ((index % 10) === 0) {
- return `Header ${index}`;
- }
- }
-
- myFooterFn: HeaderFn = (_, index) => {
- if ((index % 5) === 0) {
- return `Footer ${index}`;
- }
- }
-
- addItems() {
- console.log('adding items');
- this.items.push(
- { name: `New Item`, checked: true}
- );
- this.virtualScroll.checkEnd();
- }
-}
diff --git a/core/api.txt b/core/api.txt
index 996ca60c94..bfa38850c4 100644
--- a/core/api.txt
+++ b/core/api.txt
@@ -1461,22 +1461,4 @@ ion-toolbar,css-prop,--opacity
ion-toolbar,css-prop,--padding-bottom
ion-toolbar,css-prop,--padding-end
ion-toolbar,css-prop,--padding-start
-ion-toolbar,css-prop,--padding-top
-
-ion-virtual-scroll,none
-ion-virtual-scroll,prop,approxFooterHeight,number,30,false,false
-ion-virtual-scroll,prop,approxHeaderHeight,number,30,false,false
-ion-virtual-scroll,prop,approxItemHeight,number,45,false,false
-ion-virtual-scroll,prop,footerFn,((item: any, index: number, items: any[]) => string | null | undefined) | undefined,undefined,false,false
-ion-virtual-scroll,prop,footerHeight,((item: any, index: number) => number) | undefined,undefined,false,false
-ion-virtual-scroll,prop,headerFn,((item: any, index: number, items: any[]) => string | null | undefined) | undefined,undefined,false,false
-ion-virtual-scroll,prop,headerHeight,((item: any, index: number) => number) | undefined,undefined,false,false
-ion-virtual-scroll,prop,itemHeight,((item: any, index: number) => number) | undefined,undefined,false,false
-ion-virtual-scroll,prop,items,any[] | undefined,undefined,false,false
-ion-virtual-scroll,prop,nodeRender,((el: HTMLElement | null, cell: Cell, domIndex: number) => HTMLElement) | undefined,undefined,false,false
-ion-virtual-scroll,prop,renderFooter,((item: any, index: number) => any) | undefined,undefined,false,false
-ion-virtual-scroll,prop,renderHeader,((item: any, index: number) => any) | undefined,undefined,false,false
-ion-virtual-scroll,prop,renderItem,((item: any, index: number) => any) | undefined,undefined,false,false
-ion-virtual-scroll,method,checkEnd,checkEnd() => Promise
-ion-virtual-scroll,method,checkRange,checkRange(offset: number, len?: number) => Promise
-ion-virtual-scroll,method,positionForItem,positionForItem(index: number) => Promise
\ No newline at end of file
+ion-toolbar,css-prop,--padding-top
\ No newline at end of file
diff --git a/core/src/components.d.ts b/core/src/components.d.ts
index 9472e17678..75b2fd7dec 100644
--- a/core/src/components.d.ts
+++ b/core/src/components.d.ts
@@ -5,7 +5,7 @@
* It contains typing information for all components that exist in this project.
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
-import { AccordionGroupChangeEventDetail, ActionSheetAttributes, ActionSheetButton, AlertButton, AlertInput, AnimationBuilder, AutocompleteTypes, BreadcrumbCollapsedClickEventDetail, CheckboxChangeEventDetail, Color, ComponentProps, ComponentRef, DatetimeChangeEventDetail, DatetimePresentation, DomRenderFn, FooterHeightFn, FrameworkDelegate, HeaderFn, HeaderHeightFn, InputChangeEventDetail, ItemHeightFn, ItemRenderFn, ItemReorderEventDetail, LoadingAttributes, MenuChangeEventDetail, ModalAttributes, ModalBreakpointChangeEventDetail, ModalHandleBehavior, NavComponent, NavComponentWithProps, NavOptions, OverlayEventDetail, PickerAttributes, PickerButton, PickerColumn, PopoverAttributes, PopoverSize, PositionAlign, PositionReference, PositionSide, RadioGroupChangeEventDetail, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue, RefresherEventDetail, RouteID, RouterDirection, RouterEventDetail, RouterOutletOptions, RouteWrite, ScrollBaseDetail, ScrollDetail, SearchbarChangeEventDetail, SegmentButtonLayout, SegmentChangeEventDetail, SelectChangeEventDetail, SelectInterface, SelectPopoverOption, Side, SpinnerTypes, StyleEventDetail, SwipeGestureHandler, TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout, TextareaChangeEventDetail, TextFieldTypes, ToastButton, ToggleChangeEventDetail, TransitionDoneFn, TransitionInstruction, TriggerAction, ViewController } from "./interface";
+import { AccordionGroupChangeEventDetail, ActionSheetAttributes, ActionSheetButton, AlertButton, AlertInput, AnimationBuilder, AutocompleteTypes, BreadcrumbCollapsedClickEventDetail, CheckboxChangeEventDetail, Color, ComponentProps, ComponentRef, DatetimeChangeEventDetail, DatetimePresentation, FrameworkDelegate, InputChangeEventDetail, ItemReorderEventDetail, LoadingAttributes, MenuChangeEventDetail, ModalAttributes, ModalBreakpointChangeEventDetail, ModalHandleBehavior, NavComponent, NavComponentWithProps, NavOptions, OverlayEventDetail, PickerAttributes, PickerButton, PickerColumn, PopoverAttributes, PopoverSize, PositionAlign, PositionReference, PositionSide, RadioGroupChangeEventDetail, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue, RefresherEventDetail, RouteID, RouterDirection, RouterEventDetail, RouterOutletOptions, RouteWrite, ScrollBaseDetail, ScrollDetail, SearchbarChangeEventDetail, SegmentButtonLayout, SegmentChangeEventDetail, SelectChangeEventDetail, SelectInterface, SelectPopoverOption, Side, SpinnerTypes, StyleEventDetail, SwipeGestureHandler, TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout, TextareaChangeEventDetail, TextFieldTypes, ToastButton, ToggleChangeEventDetail, TransitionDoneFn, TransitionInstruction, TriggerAction, ViewController } from "./interface";
import { IonicSafeString } from "./utils/sanitization";
import { AlertAttributes } from "./components/alert/alert-interface";
import { CounterFormatter } from "./components/item/item-interface";
@@ -3040,73 +3040,6 @@ export namespace Components {
*/
"mode"?: "ios" | "md";
}
- interface IonVirtualScroll {
- /**
- * The approximate width of each footer template's cell. This dimension is used to help determine how many cells should be created when initialized, and to help calculate the height of the scrollable area. This height value can only use `px` units. Note that the actual rendered size of each cell comes from the app's CSS, whereas this approximation is used to help calculate initial dimensions before the item has been rendered.
- */
- "approxFooterHeight": number;
- /**
- * The approximate height of each header template's cell. This dimension is used to help determine how many cells should be created when initialized, and to help calculate the height of the scrollable area. This height value can only use `px` units. Note that the actual rendered size of each cell comes from the app's CSS, whereas this approximation is used to help calculate initial dimensions before the item has been rendered.
- */
- "approxHeaderHeight": number;
- /**
- * It is important to provide this if virtual item height will be significantly larger than the default The approximate height of each virtual item template's cell. This dimension is used to help determine how many cells should be created when initialized, and to help calculate the height of the scrollable area. This height value can only use `px` units. Note that the actual rendered size of each cell comes from the app's CSS, whereas this approximation is used to help calculate initial dimensions before the item has been rendered.
- */
- "approxItemHeight": number;
- /**
- * This method marks the tail the items array as dirty, so they can be re-rendered. It's equivalent to calling: ```js virtualScroll.checkRange(lastItemLen); ```
- */
- "checkEnd": () => Promise;
- /**
- * This method marks a subset of items as dirty, so they can be re-rendered. Items should be marked as dirty any time the content or their style changes. The subset of items to be updated can are specifying by an offset and a length.
- */
- "checkRange": (offset: number, len?: number) => Promise;
- "domRender"?: DomRenderFn;
- /**
- * Section footers and the data used within its given template can be dynamically created by passing a function to `footerFn`. The logic within the footer function can decide if the footer template should be used, and what data to give to the footer template. The function must return `null` if a footer cell shouldn't be created.
- */
- "footerFn"?: HeaderFn;
- /**
- * An optional function that maps each item footer within their height.
- */
- "footerHeight"?: FooterHeightFn;
- /**
- * Section headers and the data used within its given template can be dynamically created by passing a function to `headerFn`. For example, a large list of contacts usually has dividers between each letter in the alphabet. App's can provide their own custom `headerFn` which is called with each record within the dataset. The logic within the header function can decide if the header template should be used, and what data to give to the header template. The function must return `null` if a header cell shouldn't be created.
- */
- "headerFn"?: HeaderFn;
- /**
- * An optional function that maps each item header within their height.
- */
- "headerHeight"?: HeaderHeightFn;
- /**
- * An optional function that maps each item within their height. When this function is provides, heavy optimizations and fast path can be taked by `ion-virtual-scroll` leading to massive performance improvements. This function allows to skip all DOM reads, which can be Doing so leads to massive performance
- */
- "itemHeight"?: ItemHeightFn;
- /**
- * The data that builds the templates within the virtual scroll. It's important to note that when this data has changed, then the entire virtual scroll is reset, which is an expensive operation and should be avoided if possible.
- */
- "items"?: any[];
- /**
- * NOTE: only Vanilla JS API.
- */
- "nodeRender"?: ItemRenderFn;
- /**
- * Returns the position of the virtual item at the given index.
- */
- "positionForItem": (index: number) => Promise;
- /**
- * NOTE: only JSX API for stencil. Provide a render function for the footer to be rendered. Returns a JSX virtual-dom.
- */
- "renderFooter"?: (item: any, index: number) => any;
- /**
- * NOTE: only JSX API for stencil. Provide a render function for the header to be rendered. Returns a JSX virtual-dom.
- */
- "renderHeader"?: (item: any, index: number) => any;
- /**
- * NOTE: only JSX API for stencil. Provide a render function for the items to be rendered. Returns a JSX virtual-dom.
- */
- "renderItem"?: (item: any, index: number) => any;
- }
}
export interface IonAccordionGroupCustomEvent extends CustomEvent {
detail: T;
@@ -3855,12 +3788,6 @@ declare global {
prototype: HTMLIonToolbarElement;
new (): HTMLIonToolbarElement;
};
- interface HTMLIonVirtualScrollElement extends Components.IonVirtualScroll, HTMLStencilElement {
- }
- var HTMLIonVirtualScrollElement: {
- prototype: HTMLIonVirtualScrollElement;
- new (): HTMLIonVirtualScrollElement;
- };
interface HTMLElementTagNameMap {
"ion-accordion": HTMLIonAccordionElement;
"ion-accordion-group": HTMLIonAccordionGroupElement;
@@ -3955,7 +3882,6 @@ declare global {
"ion-toast": HTMLIonToastElement;
"ion-toggle": HTMLIonToggleElement;
"ion-toolbar": HTMLIonToolbarElement;
- "ion-virtual-scroll": HTMLIonVirtualScrollElement;
}
}
declare namespace LocalJSX {
@@ -7049,61 +6975,6 @@ declare namespace LocalJSX {
*/
"mode"?: "ios" | "md";
}
- interface IonVirtualScroll {
- /**
- * The approximate width of each footer template's cell. This dimension is used to help determine how many cells should be created when initialized, and to help calculate the height of the scrollable area. This height value can only use `px` units. Note that the actual rendered size of each cell comes from the app's CSS, whereas this approximation is used to help calculate initial dimensions before the item has been rendered.
- */
- "approxFooterHeight"?: number;
- /**
- * The approximate height of each header template's cell. This dimension is used to help determine how many cells should be created when initialized, and to help calculate the height of the scrollable area. This height value can only use `px` units. Note that the actual rendered size of each cell comes from the app's CSS, whereas this approximation is used to help calculate initial dimensions before the item has been rendered.
- */
- "approxHeaderHeight"?: number;
- /**
- * It is important to provide this if virtual item height will be significantly larger than the default The approximate height of each virtual item template's cell. This dimension is used to help determine how many cells should be created when initialized, and to help calculate the height of the scrollable area. This height value can only use `px` units. Note that the actual rendered size of each cell comes from the app's CSS, whereas this approximation is used to help calculate initial dimensions before the item has been rendered.
- */
- "approxItemHeight"?: number;
- "domRender"?: DomRenderFn;
- /**
- * Section footers and the data used within its given template can be dynamically created by passing a function to `footerFn`. The logic within the footer function can decide if the footer template should be used, and what data to give to the footer template. The function must return `null` if a footer cell shouldn't be created.
- */
- "footerFn"?: HeaderFn;
- /**
- * An optional function that maps each item footer within their height.
- */
- "footerHeight"?: FooterHeightFn;
- /**
- * Section headers and the data used within its given template can be dynamically created by passing a function to `headerFn`. For example, a large list of contacts usually has dividers between each letter in the alphabet. App's can provide their own custom `headerFn` which is called with each record within the dataset. The logic within the header function can decide if the header template should be used, and what data to give to the header template. The function must return `null` if a header cell shouldn't be created.
- */
- "headerFn"?: HeaderFn;
- /**
- * An optional function that maps each item header within their height.
- */
- "headerHeight"?: HeaderHeightFn;
- /**
- * An optional function that maps each item within their height. When this function is provides, heavy optimizations and fast path can be taked by `ion-virtual-scroll` leading to massive performance improvements. This function allows to skip all DOM reads, which can be Doing so leads to massive performance
- */
- "itemHeight"?: ItemHeightFn;
- /**
- * The data that builds the templates within the virtual scroll. It's important to note that when this data has changed, then the entire virtual scroll is reset, which is an expensive operation and should be avoided if possible.
- */
- "items"?: any[];
- /**
- * NOTE: only Vanilla JS API.
- */
- "nodeRender"?: ItemRenderFn;
- /**
- * NOTE: only JSX API for stencil. Provide a render function for the footer to be rendered. Returns a JSX virtual-dom.
- */
- "renderFooter"?: (item: any, index: number) => any;
- /**
- * NOTE: only JSX API for stencil. Provide a render function for the header to be rendered. Returns a JSX virtual-dom.
- */
- "renderHeader"?: (item: any, index: number) => any;
- /**
- * NOTE: only JSX API for stencil. Provide a render function for the items to be rendered. Returns a JSX virtual-dom.
- */
- "renderItem"?: (item: any, index: number) => any;
- }
interface IntrinsicElements {
"ion-accordion": IonAccordion;
"ion-accordion-group": IonAccordionGroup;
@@ -7198,7 +7069,6 @@ declare namespace LocalJSX {
"ion-toast": IonToast;
"ion-toggle": IonToggle;
"ion-toolbar": IonToolbar;
- "ion-virtual-scroll": IonVirtualScroll;
}
}
export { LocalJSX as JSX };
@@ -7298,7 +7168,6 @@ declare module "@stencil/core" {
"ion-toast": LocalJSX.IonToast & JSXBase.HTMLAttributes;
"ion-toggle": LocalJSX.IonToggle & JSXBase.HTMLAttributes;
"ion-toolbar": LocalJSX.IonToolbar & JSXBase.HTMLAttributes;
- "ion-virtual-scroll": LocalJSX.IonVirtualScroll & JSXBase.HTMLAttributes;
}
}
}
diff --git a/core/src/components/virtual-scroll/constants.ts b/core/src/components/virtual-scroll/constants.ts
deleted file mode 100644
index 6f05ddce7e..0000000000
--- a/core/src/components/virtual-scroll/constants.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export const CELL_TYPE_ITEM = 'item';
-export const CELL_TYPE_HEADER = 'header';
-export const CELL_TYPE_FOOTER = 'footer';
-
-export const NODE_CHANGE_NONE = 0;
-export const NODE_CHANGE_POSITION = 1;
-export const NODE_CHANGE_CELL = 2;
diff --git a/core/src/components/virtual-scroll/test/basic/e2e.ts b/core/src/components/virtual-scroll/test/basic/e2e.ts
deleted file mode 100644
index 183d21e21d..0000000000
--- a/core/src/components/virtual-scroll/test/basic/e2e.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { newE2EPage } from '@stencil/core/testing';
-
-test('virtual-scroll: basic', async () => {
- const page = await newE2EPage({
- url: '/src/components/virtual-scroll/test/basic?ionic:_testing=true',
- });
- await page.waitForTimeout(300);
-
- const compare = await page.compareScreenshot();
- expect(compare).toMatchScreenshot();
-});
diff --git a/core/src/components/virtual-scroll/test/basic/index.html b/core/src/components/virtual-scroll/test/basic/index.html
deleted file mode 100644
index a500cf5712..0000000000
--- a/core/src/components/virtual-scroll/test/basic/index.html
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
-
-
- Ionic Item Sliding
-
-
-
-
-
-
-
-
-
-
-
-
- Ionic CDN demo
-
- Add Items
-
-
-
-
-
-
-
-
-
-
- Add Items
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/core/src/components/virtual-scroll/test/cards/e2e.ts b/core/src/components/virtual-scroll/test/cards/e2e.ts
deleted file mode 100644
index f9d4db52cb..0000000000
--- a/core/src/components/virtual-scroll/test/cards/e2e.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { newE2EPage } from '@stencil/core/testing';
-
-test('virtual-scroll: cards', async () => {
- const page = await newE2EPage({
- url: '/src/components/virtual-scroll/test/cards?ionic:_testing=true',
- });
- await page.waitForTimeout(300);
-
- const compare = await page.compareScreenshot();
- expect(compare).toMatchScreenshot();
-});
diff --git a/core/src/components/virtual-scroll/test/cards/index.html b/core/src/components/virtual-scroll/test/cards/index.html
deleted file mode 100644
index ef91ad38a5..0000000000
--- a/core/src/components/virtual-scroll/test/cards/index.html
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-
-
- Ionic Item Sliding
-
-
-
-
-
-
-
-
-
-
-
-
- Ionic CDN demo
-
-
-
-
-
-
-
-
- Card Header
-
-
-
-
-
-
-
-
-
-
-
diff --git a/core/src/components/virtual-scroll/test/virtual-scroll-utils.spec.ts b/core/src/components/virtual-scroll/test/virtual-scroll-utils.spec.ts
deleted file mode 100644
index d100ee7899..0000000000
--- a/core/src/components/virtual-scroll/test/virtual-scroll-utils.spec.ts
+++ /dev/null
@@ -1,572 +0,0 @@
-import type { HeaderFn, ItemHeightFn, VirtualNode } from '../../../interface';
-import { CELL_TYPE_ITEM, CELL_TYPE_HEADER, CELL_TYPE_FOOTER } from '../constants';
-import type { Range } from '../virtual-scroll-utils';
-import {
- calcCells,
- calcHeightIndex,
- getRange,
- getShouldUpdate,
- getViewport,
- positionForIndex,
- resizeBuffer,
- updateVDom,
-} from '../virtual-scroll-utils';
-
-describe('getViewport', () => {
- it('should return viewport without margin', () => {
- expect(getViewport(0, 100, 0)).toEqual({
- top: 0,
- bottom: 100,
- });
- });
-
- it('should return viewport with margin', () => {
- expect(getViewport(0, 100, 150)).toEqual({
- top: 0,
- bottom: 250,
- });
- });
-
- it('should return viewport with margin and scrollTop', () => {
- expect(getViewport(150, 100, 150)).toEqual({
- top: 0,
- bottom: 400,
- });
- });
-
- it('should return viewport with margin and scrollTop 2', () => {
- expect(getViewport(100, 100, 10)).toEqual({
- top: 90,
- bottom: 210,
- });
- });
-});
-
-describe('getRange', () => {
- it('should return initial bounds without buffer', () => {
- const heightBuffer = mockHeightBuffer(20, () => 10);
- const bounds = getRange(heightBuffer, { top: 0, bottom: 100 }, 0);
-
- expect(bounds).toEqual({
- offset: 0,
- length: 10,
- });
- });
-
- it('should return initial bounds with buffer', () => {
- const heightBuffer = mockHeightBuffer(20, () => 10);
- const bounds = getRange(heightBuffer, { top: 0, bottom: 100 }, 4);
-
- expect(bounds).toEqual({
- offset: 0,
- length: 14,
- });
- });
-
- it('should return initial bounds truncked', () => {
- const heightBuffer = mockHeightBuffer(5, () => 10);
- const bounds = getRange(heightBuffer, { top: 0, bottom: 100 }, 4);
-
- expect(bounds).toEqual({
- offset: 0,
- length: 5,
- });
- });
-
- it('should return just first component', () => {
- const heightBuffer = mockHeightBuffer(5, () => 100);
- expect(getRange(heightBuffer, { top: 0, bottom: 100 }, 0)).toEqual({
- offset: 0,
- length: 1,
- });
-
- expect(getRange(heightBuffer, { top: 50, bottom: 100 }, 0)).toEqual({
- offset: 0,
- length: 1,
- });
-
- expect(getRange(heightBuffer, { top: 100, bottom: 200 }, 0)).toEqual({
- offset: 1,
- length: 1,
- });
- });
-
- it('should return just two components', () => {
- const heightBuffer = mockHeightBuffer(5, () => 100);
- expect(getRange(heightBuffer, { top: 1, bottom: 101 }, 0)).toEqual({
- offset: 0,
- length: 2,
- });
-
- expect(getRange(heightBuffer, { top: 99, bottom: 200 }, 0)).toEqual({
- offset: 0,
- length: 2,
- });
-
- expect(getRange(heightBuffer, { top: 100, bottom: 201 }, 0)).toEqual({
- offset: 1,
- length: 2,
- });
- });
-
- it('should return three components', () => {
- const heightBuffer = mockHeightBuffer(5, () => 100);
- expect(getRange(heightBuffer, { top: 99, bottom: 201 }, 0)).toEqual({
- offset: 0,
- length: 3,
- });
- });
-});
-
-describe('resizeBuffer', () => {
- it('should allocate a buffer', () => {
- const buf = resizeBuffer(undefined, 10);
- expect(buf.length).toEqual(10);
- });
-
- it('should not allocate a buffer', () => {
- const buf = new Uint32Array(10);
- const buf2 = resizeBuffer(buf, 10);
- expect(buf).toBe(buf2);
- expect(buf.length).toEqual(10);
- });
-
- it('should grow a buffer', () => {
- const buf = new Uint32Array(10);
- buf[0] = 100;
- buf[9] = 123;
-
- const buf2 = resizeBuffer(buf, 12);
- expect(buf2.length).toEqual(12);
- expect(buf2[0]).toEqual(100);
- expect(buf2[9]).toEqual(123);
- });
-
- it('should shrink a buffer', () => {
- const buf = new Uint32Array(10);
- buf[0] = 100;
- buf[9] = 123;
-
- const buf2 = resizeBuffer(buf, 5);
- expect(buf2.length).toEqual(5);
- expect(buf2[0]).toEqual(100);
- });
-});
-
-describe('calcCells', () => {
- it('should calculate cells without headers and itemHeight', () => {
- const items = ['0', 2, 'hola', { data: 'hello' }];
- const cells = calcCells(
- items,
- undefined,
- undefined,
- undefined,
- undefined,
- undefined,
- 10,
- 20,
- 30,
- 0,
- 0,
- items.length
- );
- expect(cells).toEqual([
- {
- type: CELL_TYPE_ITEM,
- value: '0',
- i: 0,
- index: 0,
- height: 30,
- reads: 2,
- visible: false,
- },
- {
- type: CELL_TYPE_ITEM,
- value: 2,
- i: 1,
- index: 1,
- height: 30,
- reads: 2,
- visible: false,
- },
- {
- type: CELL_TYPE_ITEM,
- value: 'hola',
- i: 2,
- index: 2,
- height: 30,
- reads: 2,
- visible: false,
- },
- {
- type: CELL_TYPE_ITEM,
- value: { data: 'hello' },
- i: 3,
- index: 3,
- height: 30,
- reads: 2,
- visible: false,
- },
- ]);
- });
-
- it('should calculate cells with itemHeight', () => {
- const items = [10, 9, 8];
- let called = 0;
- const itemHeight: ItemHeightFn = (item: any, index: number) => {
- expect(item).toEqual(items[index]);
- called++;
- return index * 20 + 20;
- };
- const cells = calcCells(
- items,
- itemHeight,
- undefined,
- undefined,
- undefined,
- undefined,
- 10,
- 20,
- 30,
- 0,
- 0,
- items.length
- );
-
- expect(called).toEqual(3);
- expect(cells).toEqual([
- {
- type: CELL_TYPE_ITEM,
- value: 10,
- i: 0,
- index: 0,
- height: 20,
- reads: 0,
- visible: true,
- },
- {
- type: CELL_TYPE_ITEM,
- value: 9,
- i: 1,
- index: 1,
- height: 40,
- reads: 0,
- visible: true,
- },
- {
- type: CELL_TYPE_ITEM,
- value: 8,
- i: 2,
- index: 2,
- height: 60,
- reads: 0,
- visible: true,
- },
- ]);
- });
-
- it('should calculate cells with header and footer', () => {
- let headerCalled = 0;
- let footerCalled = 0;
- let called = 0;
- const items = ['10', '9', '8'];
- const headerFn: HeaderFn = (item, index, allItems) => {
- expect(item).toEqual(items[index]);
- expect(items).toBe(allItems);
- headerCalled++;
- return index === 0 ? 'my header' : null;
- };
- const footerFn: HeaderFn = (item, index, allItems) => {
- expect(item).toEqual(items[index]);
- expect(items).toBe(allItems);
- footerCalled++;
- return index === 2 ? 'my footer' : null;
- };
- const itemHeight: ItemHeightFn = (item: any, index: number) => {
- expect(item).toEqual(items[index]);
- called++;
- return index * 20 + 20;
- };
- const cells = calcCells(
- items,
- itemHeight,
- undefined,
- undefined,
- headerFn,
- footerFn,
- 10,
- 20,
- 30,
- 0,
- 0,
- items.length
- );
- expect(cells).toHaveLength(5);
- expect(called).toEqual(3);
- expect(headerCalled).toEqual(3);
- expect(footerCalled).toEqual(3);
- expect(cells).toEqual([
- {
- type: CELL_TYPE_HEADER,
- value: 'my header',
- i: 0,
- index: 0,
- height: 10,
- reads: 2,
- visible: false,
- },
- {
- type: CELL_TYPE_ITEM,
- value: '10',
- i: 1,
- index: 0,
- height: 20,
- reads: 0,
- visible: true,
- },
- {
- type: CELL_TYPE_ITEM,
- value: '9',
- i: 2,
- index: 1,
- height: 40,
- reads: 0,
- visible: true,
- },
- {
- type: CELL_TYPE_ITEM,
- value: '8',
- i: 3,
- index: 2,
- height: 60,
- reads: 0,
- visible: true,
- },
- {
- type: CELL_TYPE_FOOTER,
- value: 'my footer',
- i: 4,
- index: 2,
- height: 20,
- reads: 2,
- visible: false,
- },
- ]);
- });
-});
-
-describe('calcHeightIndex', () => {
- it('should generate height index', () => {
- const items = [1, 2, 3, 4, 5];
- const headerFn: HeaderFn = (_, index) => {
- return index === 0 ? 'my header' : null;
- };
- const footerFn: HeaderFn = (_, index) => {
- return index === 2 ? 'my footer' : null;
- };
- const cells = calcCells(items, undefined, undefined, undefined, headerFn, footerFn, 10, 20, 50, 0, 0, items.length);
- const buf = resizeBuffer(undefined, cells.length);
- const totalHeight = calcHeightIndex(buf, cells, 0);
- expect(buf.length).toEqual(7);
- expect(buf[0]).toEqual(0);
- expect(buf[1]).toEqual(10);
- expect(buf[2]).toEqual(60);
- expect(buf[3]).toEqual(110);
- expect(buf[4]).toEqual(160);
- expect(buf[5]).toEqual(180);
- expect(buf[6]).toEqual(230);
- expect(totalHeight).toEqual(280);
- });
-});
-
-describe('getShouldUpdate', () => {
- it('should return true if the range does not match', () => {
- expect(getShouldUpdate(Infinity, { offset: 1, length: 2 }, { offset: 1, length: 3 })).toBeTruthy();
- expect(getShouldUpdate(Infinity, { offset: 1, length: 2 }, { offset: 0, length: 2 })).toBeTruthy();
- });
-
- it('should return true if the dirty index <= bottom', () => {
- expect(getShouldUpdate(9, { offset: 1, length: 8 }, { offset: 1, length: 8 })).toBeTruthy();
- });
-
- it('should return false if the dirty index > bottom', () => {
- expect(getShouldUpdate(10, { offset: 1, length: 8 }, { offset: 1, length: 8 })).toBeFalsy();
- });
-
- it('should return false if the range matches', () => {
- expect(getShouldUpdate(Infinity, { offset: 1, length: 2 }, { offset: 1, length: 2 })).toBeFalsy();
- });
-});
-
-describe('positionForIndex', () => {
- it('should return the correct position', () => {
- const items = [1, 2, 3, 4];
- const { cells, heightIndex } = mockVirtualScroll(
- items,
- () => 40,
- (_, i) => (i === 1 ? 'hola' : null),
- (_, i) => (i === 2 ? 'hola' : null)
- );
- expect(positionForIndex(0, cells, heightIndex)).toEqual(0);
- expect(positionForIndex(1, cells, heightIndex)).toEqual(50);
- expect(positionForIndex(2, cells, heightIndex)).toEqual(90);
- expect(positionForIndex(3, cells, heightIndex)).toEqual(140);
- });
-});
-
-describe('updateVDom', () => {
- it('should initialize empty VDOM', () => {
- const vdom: VirtualNode[] = [];
- const items = [1, 2, 3, 4, 5];
- const { heightIndex, cells } = mockVirtualScroll(
- items,
- () => 20,
- (_, i) => (i === 1 ? 'hola' : null),
- (_, i) => (i === 2 ? 'hola' : null)
- );
- const range: Range = { offset: 1, length: 6 };
-
- updateVDom(vdom, heightIndex, cells, range);
- expect(vdom).toEqual([
- { cell: cells[1], change: 2, d: false, top: 20, visible: true },
- { cell: cells[2], change: 2, d: false, top: 30, visible: true },
- { cell: cells[3], change: 2, d: false, top: 50, visible: true },
- { cell: cells[4], change: 2, d: false, top: 70, visible: true },
- { cell: cells[5], change: 2, d: false, top: 80, visible: true },
- { cell: cells[6], change: 2, d: false, top: 100, visible: true },
- ]);
- });
-
- it('should simulate real scrolling', () => {
- const vdom: VirtualNode[] = [];
- const items = Array.from({ length: 100 }, (_, i) => i + '');
- const { heightIndex, cells } = mockVirtualScroll(items, () => 20);
- updateVDom(vdom, heightIndex, cells, { offset: 0, length: 4 });
- updateVDom(vdom, heightIndex, cells, { offset: 0, length: 4 });
- expect(vdom).toEqual([
- { cell: cells[0], change: 0, d: false, top: 0, visible: true },
- { cell: cells[1], change: 0, d: false, top: 20, visible: true },
- { cell: cells[2], change: 0, d: false, top: 40, visible: true },
- { cell: cells[3], change: 0, d: false, top: 60, visible: true },
- ]);
-
- updateVDom(vdom, heightIndex, cells, { offset: 0, length: 5 });
- expect(vdom).toEqual([
- { cell: cells[0], change: 0, d: false, top: 0, visible: true },
- { cell: cells[1], change: 0, d: false, top: 20, visible: true },
- { cell: cells[2], change: 0, d: false, top: 40, visible: true },
- { cell: cells[3], change: 0, d: false, top: 60, visible: true },
- { cell: cells[4], change: 2, d: false, top: 80, visible: true },
- ]);
-
- updateVDom(vdom, heightIndex, cells, { offset: 1, length: 4 });
- expect(vdom).toEqual([
- { cell: cells[0], change: 1, d: true, top: -9999, visible: true },
- { cell: cells[1], change: 0, d: false, top: 20, visible: true },
- { cell: cells[2], change: 0, d: false, top: 40, visible: true },
- { cell: cells[3], change: 0, d: false, top: 60, visible: true },
- { cell: cells[4], change: 0, d: false, top: 80, visible: true },
- ]);
-
- updateVDom(vdom, heightIndex, cells, { offset: 1, length: 5 });
- expect(vdom).toEqual([
- { cell: cells[5], change: 2, d: false, top: 100, visible: true },
- { cell: cells[1], change: 0, d: false, top: 20, visible: true },
- { cell: cells[2], change: 0, d: false, top: 40, visible: true },
- { cell: cells[3], change: 0, d: false, top: 60, visible: true },
- { cell: cells[4], change: 0, d: false, top: 80, visible: true },
- ]);
-
- updateVDom(vdom, heightIndex, cells, { offset: 2, length: 5 });
- expect(vdom).toEqual([
- { cell: cells[5], change: 0, d: false, top: 100, visible: true },
- { cell: cells[6], change: 2, d: false, top: 120, visible: true },
- { cell: cells[2], change: 0, d: false, top: 40, visible: true },
- { cell: cells[3], change: 0, d: false, top: 60, visible: true },
- { cell: cells[4], change: 0, d: false, top: 80, visible: true },
- ]);
-
- updateVDom(vdom, heightIndex, cells, { offset: 10, length: 6 });
- expect(vdom).toEqual([
- { cell: cells[10], change: 2, d: false, top: 200, visible: true },
- { cell: cells[11], change: 2, d: false, top: 220, visible: true },
- { cell: cells[12], change: 2, d: false, top: 240, visible: true },
- { cell: cells[13], change: 2, d: false, top: 260, visible: true },
- { cell: cells[14], change: 2, d: false, top: 280, visible: true },
- { cell: cells[15], change: 2, d: false, top: 300, visible: true },
- ]);
-
- updateVDom(vdom, heightIndex, cells, { offset: 13, length: 10 });
- expect(vdom).toEqual([
- { cell: cells[16], change: 2, d: false, top: 320, visible: true },
- { cell: cells[17], change: 2, d: false, top: 340, visible: true },
- { cell: cells[18], change: 2, d: false, top: 360, visible: true },
- { cell: cells[13], change: 0, d: false, top: 260, visible: true },
- { cell: cells[14], change: 0, d: false, top: 280, visible: true },
- { cell: cells[15], change: 0, d: false, top: 300, visible: true },
- { cell: cells[19], change: 2, d: false, top: 380, visible: true },
- { cell: cells[20], change: 2, d: false, top: 400, visible: true },
- { cell: cells[21], change: 2, d: false, top: 420, visible: true },
- { cell: cells[22], change: 2, d: false, top: 440, visible: true },
- ]);
-
- updateVDom(vdom, heightIndex, cells, { offset: 13, length: 1 });
- expect(vdom).toEqual([
- { cell: cells[16], change: 1, d: true, top: -9999, visible: true },
- { cell: cells[17], change: 1, d: true, top: -9999, visible: true },
- { cell: cells[18], change: 1, d: true, top: -9999, visible: true },
- { cell: cells[13], change: 0, d: false, top: 260, visible: true },
- { cell: cells[14], change: 1, d: true, top: -9999, visible: true },
- { cell: cells[15], change: 1, d: true, top: -9999, visible: true },
- { cell: cells[19], change: 1, d: true, top: -9999, visible: true },
- { cell: cells[20], change: 1, d: true, top: -9999, visible: true },
- { cell: cells[21], change: 1, d: true, top: -9999, visible: true },
- { cell: cells[22], change: 1, d: true, top: -9999, visible: true },
- ]);
-
- updateVDom(vdom, heightIndex, cells, { offset: 13, length: 1 });
- expect(vdom).toEqual([
- { cell: cells[16], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[17], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[18], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[13], change: 0, d: false, top: 260, visible: true },
- { cell: cells[14], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[15], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[19], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[20], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[21], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[22], change: 0, d: true, top: -9999, visible: true },
- ]);
-
- updateVDom(vdom, heightIndex, cells, { offset: 0, length: 1 });
- expect(vdom).toEqual([
- { cell: cells[0], change: 2, d: false, top: 0, visible: true },
- { cell: cells[17], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[18], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[13], change: 1, d: true, top: -9999, visible: true },
- { cell: cells[14], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[15], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[19], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[20], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[21], change: 0, d: true, top: -9999, visible: true },
- { cell: cells[22], change: 0, d: true, top: -9999, visible: true },
- ]);
- });
-});
-
-function mockVirtualScroll(items: any[], itemHeight?: ItemHeightFn, headerFn?: HeaderFn, footerFn?: HeaderFn) {
- const cells = calcCells(items, itemHeight, undefined, undefined, headerFn, footerFn, 10, 10, 30, 0, 0, items.length);
- const heightIndex = resizeBuffer(undefined, cells.length);
- calcHeightIndex(heightIndex, cells, 0);
- return { items, heightIndex, cells };
-}
-
-function mockHeightBuffer(size: number, step: (index: number) => number) {
- const buf = new Uint32Array(size);
- let acum = 0;
- for (let i = 0; i < size; i++) {
- buf[i] = acum;
- acum += step(i);
- }
- return buf;
-}
diff --git a/core/src/components/virtual-scroll/virtual-scroll-interface.ts b/core/src/components/virtual-scroll/virtual-scroll-interface.ts
deleted file mode 100644
index 30b812cac4..0000000000
--- a/core/src/components/virtual-scroll/virtual-scroll-interface.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-export interface Cell {
- i: number;
- index: number;
- value: any;
- type: CellType;
- height: number;
- reads: number;
- visible: boolean;
-}
-
-export interface VirtualNode {
- cell: Cell;
- top: number;
- change: NodeChange;
- d: boolean;
- visible: boolean;
-}
-
-export type CellType = 'item' | 'header' | 'footer';
-export type NodeChange = number;
-export type HeaderFn = (item: any, index: number, items: any[]) => string | null | undefined;
-export type ItemHeightFn = (item: any, index: number) => number;
-export type HeaderHeightFn = (item: any, index: number) => number;
-export type FooterHeightFn = (item: any, index: number) => number;
-export type ItemRenderFn = (el: HTMLElement | null, cell: Cell, domIndex: number) => HTMLElement;
-export type DomRenderFn = (dom: VirtualNode[]) => void;
diff --git a/core/src/components/virtual-scroll/virtual-scroll-utils.ts b/core/src/components/virtual-scroll/virtual-scroll-utils.ts
deleted file mode 100644
index e6a6f7dc1d..0000000000
--- a/core/src/components/virtual-scroll/virtual-scroll-utils.ts
+++ /dev/null
@@ -1,304 +0,0 @@
-import type { Cell, HeaderFn, ItemHeightFn, ItemRenderFn, VirtualNode } from '../../interface';
-
-import {
- CELL_TYPE_FOOTER,
- CELL_TYPE_HEADER,
- CELL_TYPE_ITEM,
- NODE_CHANGE_CELL,
- NODE_CHANGE_NONE,
- NODE_CHANGE_POSITION,
-} from './constants';
-import type { CellType, FooterHeightFn, HeaderHeightFn } from './virtual-scroll-interface';
-
-export interface Viewport {
- top: number;
- bottom: number;
-}
-
-export interface Range {
- offset: number;
- length: number;
-}
-
-const MIN_READS = 2;
-
-export const updateVDom = (dom: VirtualNode[], heightIndex: Uint32Array, cells: Cell[], range: Range) => {
- // reset dom
- for (const node of dom) {
- node.change = NODE_CHANGE_NONE;
- node.d = true;
- }
-
- // try to match into exisiting dom
- const toMutate = [];
- const end = range.offset + range.length;
-
- for (let i = range.offset; i < end; i++) {
- const cell = cells[i];
- const node = dom.find((n) => n.d && n.cell === cell);
- if (node) {
- const top = heightIndex[i];
- if (top !== node.top) {
- node.top = top;
- node.change = NODE_CHANGE_POSITION;
- }
- node.d = false;
- } else {
- toMutate.push(cell);
- }
- }
-
- // needs to append
- const pool = dom.filter((n) => n.d);
-
- for (const cell of toMutate) {
- const node = pool.find((n) => n.d && n.cell.type === cell.type);
- const index = cell.i;
- if (node) {
- node.d = false;
- node.change = NODE_CHANGE_CELL;
- node.cell = cell;
- node.top = heightIndex[index];
- } else {
- dom.push({
- d: false,
- cell,
- visible: true,
- change: NODE_CHANGE_CELL,
- top: heightIndex[index],
- });
- }
- }
- dom
- .filter((n) => n.d && n.top !== -9999)
- .forEach((n) => {
- n.change = NODE_CHANGE_POSITION;
- n.top = -9999;
- });
-};
-
-export const doRender = (
- el: HTMLElement,
- nodeRender: ItemRenderFn,
- dom: VirtualNode[],
- updateCellHeight: (cell: Cell, node: HTMLElement) => void
-) => {
- const children = Array.from(el.children).filter((n) => n.tagName !== 'TEMPLATE');
- const childrenNu = children.length;
- let child: HTMLElement;
- for (let i = 0; i < dom.length; i++) {
- const node = dom[i];
- const cell = node.cell;
-
- // the cell change, the content must be updated
- if (node.change === NODE_CHANGE_CELL) {
- if (i < childrenNu) {
- child = children[i] as HTMLElement;
- nodeRender(child, cell, i);
- } else {
- const newChild = createNode(el, cell.type);
- child = nodeRender(newChild, cell, i) || newChild;
- child.classList.add('virtual-item');
- el.appendChild(child!);
- }
- (child as any)['$ionCell'] = cell;
- } else {
- child = children[i] as HTMLElement;
- }
-
- // only update position when it changes
- if (node.change !== NODE_CHANGE_NONE) {
- child.style.transform = `translate3d(0,${node.top}px,0)`;
- }
-
- // update visibility
- const visible = cell.visible;
- if (node.visible !== visible) {
- if (visible) {
- child.classList.remove('virtual-loading');
- } else {
- child.classList.add('virtual-loading');
- }
- node.visible = visible;
- }
-
- // dynamic height
- if (cell.reads > 0) {
- updateCellHeight(cell, child);
- cell.reads--;
- }
- }
-};
-
-const createNode = (el: HTMLElement, type: CellType): HTMLElement | null => {
- const template = getTemplate(el, type);
- if (template && el.ownerDocument) {
- return el.ownerDocument.importNode(template.content, true).children[0] as HTMLElement;
- }
- return null;
-};
-
-const getTemplate = (el: HTMLElement, type: CellType): HTMLTemplateElement | null => {
- switch (type) {
- case CELL_TYPE_ITEM:
- return el.querySelector('template:not([name])');
- case CELL_TYPE_HEADER:
- return el.querySelector('template[name=header]');
- case CELL_TYPE_FOOTER:
- return el.querySelector('template[name=footer]');
- }
-};
-
-export const getViewport = (scrollTop: number, vierportHeight: number, margin: number): Viewport => {
- return {
- top: Math.max(scrollTop - margin, 0),
- bottom: scrollTop + vierportHeight + margin,
- };
-};
-
-export const getRange = (heightIndex: Uint32Array, viewport: Viewport, buffer: number): Range => {
- const topPos = viewport.top;
- const bottomPos = viewport.bottom;
-
- // find top index
- let i = 0;
- for (; i < heightIndex.length; i++) {
- if (heightIndex[i] > topPos) {
- break;
- }
- }
- const offset = Math.max(i - buffer - 1, 0);
-
- // find bottom index
- for (; i < heightIndex.length; i++) {
- if (heightIndex[i] >= bottomPos) {
- break;
- }
- }
-
- const end = Math.min(i + buffer, heightIndex.length);
- const length = end - offset;
- return { offset, length };
-};
-
-export const getShouldUpdate = (dirtyIndex: number, currentRange: Range, range: Range) => {
- const end = range.offset + range.length;
- return dirtyIndex <= end || currentRange.offset !== range.offset || currentRange.length !== range.length;
-};
-
-export const findCellIndex = (cells: Cell[], index: number): number => {
- const max = cells.length > 0 ? cells[cells.length - 1].index : 0;
- if (index === 0) {
- return 0;
- } else if (index === max + 1) {
- return cells.length;
- } else {
- return cells.findIndex((c) => c.index === index);
- }
-};
-
-export const inplaceUpdate = (dst: Cell[], src: Cell[], offset: number) => {
- if (offset === 0 && src.length >= dst.length) {
- return src;
- }
- for (let i = 0; i < src.length; i++) {
- dst[i + offset] = src[i];
- }
- return dst;
-};
-
-export const calcCells = (
- items: any[],
-
- itemHeight: ItemHeightFn | undefined,
- headerHeight: HeaderHeightFn | undefined,
- footerHeight: FooterHeightFn | undefined,
- headerFn: HeaderFn | undefined,
- footerFn: HeaderFn | undefined,
-
- approxHeaderHeight: number,
- approxFooterHeight: number,
- approxItemHeight: number,
-
- j: number,
- offset: number,
- len: number
-): Cell[] => {
- const cells: Cell[] = [];
- const end = len + offset;
- for (let i = offset; i < end; i++) {
- const item = items[i];
- if (headerFn) {
- const value = headerFn(item, i, items);
- if (value != null) {
- cells.push({
- i: j++,
- type: CELL_TYPE_HEADER,
- value,
- index: i,
- height: headerHeight ? headerHeight(value, i) : approxHeaderHeight,
- reads: headerHeight ? 0 : MIN_READS,
- visible: !!headerHeight,
- });
- }
- }
-
- cells.push({
- i: j++,
- type: CELL_TYPE_ITEM,
- value: item,
- index: i,
- height: itemHeight ? itemHeight(item, i) : approxItemHeight,
- reads: itemHeight ? 0 : MIN_READS,
- visible: !!itemHeight,
- });
-
- if (footerFn) {
- const value = footerFn(item, i, items);
- if (value != null) {
- cells.push({
- i: j++,
- type: CELL_TYPE_FOOTER,
- value,
- index: i,
- height: footerHeight ? footerHeight(value, i) : approxFooterHeight,
- reads: footerHeight ? 0 : MIN_READS,
- visible: !!footerHeight,
- });
- }
- }
- }
- return cells;
-};
-
-export const calcHeightIndex = (buf: Uint32Array, cells: Cell[], index: number): number => {
- let acum = buf[index];
- for (let i = index; i < buf.length; i++) {
- buf[i] = acum;
- acum += cells[i].height;
- }
- return acum;
-};
-
-export const resizeBuffer = (buf: Uint32Array | undefined, len: number) => {
- if (!buf) {
- return new Uint32Array(len);
- }
- if (buf.length === len) {
- return buf;
- } else if (len > buf.length) {
- const newBuf = new Uint32Array(len);
- newBuf.set(buf);
- return newBuf;
- } else {
- return buf.subarray(0, len);
- }
-};
-
-export const positionForIndex = (index: number, cells: Cell[], heightIndex: Uint32Array): number => {
- const cell = cells.find((c) => c.type === CELL_TYPE_ITEM && c.index === index);
- if (cell) {
- return heightIndex[cell.i];
- }
- return -1;
-};
diff --git a/core/src/components/virtual-scroll/virtual-scroll.scss b/core/src/components/virtual-scroll/virtual-scroll.scss
deleted file mode 100644
index 00ff1c872c..0000000000
--- a/core/src/components/virtual-scroll/virtual-scroll.scss
+++ /dev/null
@@ -1,28 +0,0 @@
-@import "../../themes/ionic.globals";
-
-ion-virtual-scroll {
- display: block;
- position: relative;
-
- width: 100%;
-
- contain: strict;
-
- user-select: none;
-}
-
-ion-virtual-scroll > .virtual-loading {
- opacity: 0;
-}
-
-ion-virtual-scroll > .virtual-item {
- /* stylelint-disable declaration-no-important, property-disallowed-list */
- position: absolute !important;
-
- top: 0 !important;
- right: 0 !important;
- left: 0 !important;
-
- transition-duration: 0ms;
- will-change: transform;
-}
diff --git a/core/src/components/virtual-scroll/virtual-scroll.tsx b/core/src/components/virtual-scroll/virtual-scroll.tsx
deleted file mode 100644
index 9ad6bb9f6b..0000000000
--- a/core/src/components/virtual-scroll/virtual-scroll.tsx
+++ /dev/null
@@ -1,501 +0,0 @@
-import type { ComponentInterface, FunctionalComponent } from '@stencil/core';
-import {
- Component,
- Element,
- Host,
- Listen,
- Method,
- Prop,
- State,
- Watch,
- forceUpdate,
- h,
- readTask,
- writeTask,
-} from '@stencil/core';
-
-import type {
- Cell,
- DomRenderFn,
- FooterHeightFn,
- HeaderFn,
- HeaderHeightFn,
- ItemHeightFn,
- ItemRenderFn,
- VirtualNode,
-} from '../../interface';
-import { componentOnReady } from '../../utils/helpers';
-
-import { CELL_TYPE_FOOTER, CELL_TYPE_HEADER, CELL_TYPE_ITEM } from './constants';
-import type { Range } from './virtual-scroll-utils';
-import {
- calcCells,
- calcHeightIndex,
- doRender,
- findCellIndex,
- getRange,
- getShouldUpdate,
- getViewport,
- inplaceUpdate,
- positionForIndex,
- resizeBuffer,
- updateVDom,
-} from './virtual-scroll-utils';
-
-@Component({
- tag: 'ion-virtual-scroll',
- styleUrl: 'virtual-scroll.scss',
-})
-export class VirtualScroll implements ComponentInterface {
- private contentEl?: HTMLElement;
- private scrollEl?: HTMLElement;
- private range: Range = { offset: 0, length: 0 };
- private timerUpdate: any;
- private heightIndex?: Uint32Array;
- private viewportHeight = 0;
- private cells: Cell[] = [];
- private virtualDom: VirtualNode[] = [];
- private isEnabled = false;
- private viewportOffset = 0;
- private currentScrollTop = 0;
- private indexDirty = 0;
- private lastItemLen = 0;
- private rmEvent: (() => void) | undefined;
-
- @Element() el!: HTMLIonVirtualScrollElement;
-
- @State() totalHeight = 0;
-
- /**
- * It is important to provide this
- * if virtual item height will be significantly larger than the default
- * The approximate height of each virtual item template's cell.
- * This dimension is used to help determine how many cells should
- * be created when initialized, and to help calculate the height of
- * the scrollable area. This height value can only use `px` units.
- * Note that the actual rendered size of each cell comes from the
- * app's CSS, whereas this approximation is used to help calculate
- * initial dimensions before the item has been rendered.
- */
- @Prop() approxItemHeight = 45;
-
- /**
- * The approximate height of each header template's cell.
- * This dimension is used to help determine how many cells should
- * be created when initialized, and to help calculate the height of
- * the scrollable area. This height value can only use `px` units.
- * Note that the actual rendered size of each cell comes from the
- * app's CSS, whereas this approximation is used to help calculate
- * initial dimensions before the item has been rendered.
- */
- @Prop() approxHeaderHeight = 30;
-
- /**
- * The approximate width of each footer template's cell.
- * This dimension is used to help determine how many cells should
- * be created when initialized, and to help calculate the height of
- * the scrollable area. This height value can only use `px` units.
- * Note that the actual rendered size of each cell comes from the
- * app's CSS, whereas this approximation is used to help calculate
- * initial dimensions before the item has been rendered.
- */
- @Prop() approxFooterHeight = 30;
-
- /**
- * Section headers and the data used within its given
- * template can be dynamically created by passing a function to `headerFn`.
- * For example, a large list of contacts usually has dividers between each
- * letter in the alphabet. App's can provide their own custom `headerFn`
- * which is called with each record within the dataset. The logic within
- * the header function can decide if the header template should be used,
- * and what data to give to the header template. The function must return
- * `null` if a header cell shouldn't be created.
- */
- @Prop() headerFn?: HeaderFn;
-
- /**
- * Section footers and the data used within its given
- * template can be dynamically created by passing a function to `footerFn`.
- * The logic within the footer function can decide if the footer template
- * should be used, and what data to give to the footer template. The function
- * must return `null` if a footer cell shouldn't be created.
- */
- @Prop() footerFn?: HeaderFn;
-
- /**
- * The data that builds the templates within the virtual scroll.
- * It's important to note that when this data has changed, then the
- * entire virtual scroll is reset, which is an expensive operation and
- * should be avoided if possible.
- */
- @Prop() items?: any[];
-
- /**
- * An optional function that maps each item within their height.
- * When this function is provides, heavy optimizations and fast path can be taked by
- * `ion-virtual-scroll` leading to massive performance improvements.
- *
- * This function allows to skip all DOM reads, which can be Doing so leads
- * to massive performance
- */
- @Prop() itemHeight?: ItemHeightFn;
-
- /**
- * An optional function that maps each item header within their height.
- */
- @Prop() headerHeight?: HeaderHeightFn;
-
- /**
- * An optional function that maps each item footer within their height.
- */
- @Prop() footerHeight?: FooterHeightFn;
-
- /**
- * NOTE: only JSX API for stencil.
- *
- * Provide a render function for the items to be rendered. Returns a JSX virtual-dom.
- */
- @Prop() renderItem?: (item: any, index: number) => any;
-
- /**
- * NOTE: only JSX API for stencil.
- *
- * Provide a render function for the header to be rendered. Returns a JSX virtual-dom.
- */
- @Prop() renderHeader?: (item: any, index: number) => any;
-
- /**
- * NOTE: only JSX API for stencil.
- *
- * Provide a render function for the footer to be rendered. Returns a JSX virtual-dom.
- */
- @Prop() renderFooter?: (item: any, index: number) => any;
-
- /**
- * NOTE: only Vanilla JS API.
- */
- @Prop() nodeRender?: ItemRenderFn;
-
- /** @internal */
- @Prop() domRender?: DomRenderFn;
-
- @Watch('itemHeight')
- @Watch('headerHeight')
- @Watch('footerHeight')
- @Watch('items')
- itemsChanged() {
- this.calcCells();
- this.updateVirtualScroll();
- }
-
- componentWillLoad() {
- console.warn(
- `[Deprecation Warning]: ion-virtual-scroll has been deprecated and will be removed in Ionic Framework v7.0. See https://ionicframework.com/docs/angular/virtual-scroll for migration steps.`
- );
- }
-
- async connectedCallback() {
- const contentEl = this.el.closest('ion-content');
- if (!contentEl) {
- console.error(' must be used inside an ');
- return;
- }
- this.scrollEl = await contentEl.getScrollElement();
- this.contentEl = contentEl;
- this.calcCells();
- this.updateState();
- }
-
- componentDidUpdate() {
- this.updateState();
- }
-
- disconnectedCallback() {
- this.scrollEl = undefined;
- }
-
- @Listen('resize', { target: 'window' })
- onResize() {
- this.calcCells();
- this.updateVirtualScroll();
- }
-
- /**
- * Returns the position of the virtual item at the given index.
- */
- @Method()
- positionForItem(index: number): Promise {
- return Promise.resolve(positionForIndex(index, this.cells, this.getHeightIndex()));
- }
-
- /**
- * This method marks a subset of items as dirty, so they can be re-rendered. Items should be marked as
- * dirty any time the content or their style changes.
- *
- * The subset of items to be updated can are specifying by an offset and a length.
- */
- @Method()
- async checkRange(offset: number, len = -1) {
- // TODO: kind of hacky how we do in-place updated of the cells
- // array. this part needs a complete refactor
- if (!this.items) {
- return;
- }
- const length = len === -1 ? this.items.length - offset : len;
-
- const cellIndex = findCellIndex(this.cells, offset);
- const cells = calcCells(
- this.items,
- this.itemHeight,
- this.headerHeight,
- this.footerHeight,
- this.headerFn,
- this.footerFn,
- this.approxHeaderHeight,
- this.approxFooterHeight,
- this.approxItemHeight,
- cellIndex,
- offset,
- length
- );
- this.cells = inplaceUpdate(this.cells, cells, cellIndex);
- this.lastItemLen = this.items.length;
- this.indexDirty = Math.max(offset - 1, 0);
-
- this.scheduleUpdate();
- }
-
- /**
- * This method marks the tail the items array as dirty, so they can be re-rendered.
- *
- * It's equivalent to calling:
- *
- * ```js
- * virtualScroll.checkRange(lastItemLen);
- * ```
- */
- @Method()
- async checkEnd() {
- if (this.items) {
- this.checkRange(this.lastItemLen);
- }
- }
-
- private onScroll = () => {
- this.updateVirtualScroll();
- };
-
- private updateVirtualScroll() {
- // do nothing if virtual-scroll is disabled
- if (!this.isEnabled || !this.scrollEl) {
- return;
- }
-
- // unschedule future updates
- if (this.timerUpdate) {
- clearTimeout(this.timerUpdate);
- this.timerUpdate = undefined;
- }
-
- // schedule DOM operations into the stencil queue
- readTask(this.readVS.bind(this));
- writeTask(this.writeVS.bind(this));
- }
-
- private readVS() {
- const { contentEl, scrollEl, el } = this;
- let topOffset = 0;
- let node: HTMLElement | null = el;
- while (node && node !== contentEl) {
- topOffset += node.offsetTop;
- node = node.offsetParent as HTMLElement;
- }
- this.viewportOffset = topOffset;
- if (scrollEl) {
- this.viewportHeight = scrollEl.offsetHeight;
- this.currentScrollTop = scrollEl.scrollTop;
- }
- }
-
- private writeVS() {
- const dirtyIndex = this.indexDirty;
-
- // get visible viewport
- const scrollTop = this.currentScrollTop - this.viewportOffset;
- const viewport = getViewport(scrollTop, this.viewportHeight, 100);
-
- // compute lazily the height index
- const heightIndex = this.getHeightIndex();
-
- // get array bounds of visible cells base in the viewport
- const range = getRange(heightIndex, viewport, 2);
-
- // fast path, do nothing
- const shouldUpdate = getShouldUpdate(dirtyIndex, this.range, range);
- if (!shouldUpdate) {
- return;
- }
- this.range = range;
-
- // in place mutation of the virtual DOM
- updateVDom(this.virtualDom, heightIndex, this.cells, range);
-
- // Write DOM
- // Different code paths taken depending of the render API used
- if (this.nodeRender) {
- doRender(this.el, this.nodeRender, this.virtualDom, this.updateCellHeight.bind(this));
- } else if (this.domRender) {
- this.domRender(this.virtualDom);
- } else if (this.renderItem) {
- forceUpdate(this);
- }
- }
-
- private updateCellHeight(cell: Cell, node: any) {
- const update = () => {
- if ((node as any)['$ionCell'] === cell) {
- const style = window.getComputedStyle(node);
- const height = node.offsetHeight + parseFloat(style.getPropertyValue('margin-bottom'));
- this.setCellHeight(cell, height);
- }
- };
- if (node) {
- componentOnReady(node, update);
- } else {
- update();
- }
- }
-
- private setCellHeight(cell: Cell, height: number) {
- const index = cell.i;
- // the cell might changed since the height update was scheduled
- if (cell !== this.cells[index]) {
- return;
- }
- if (cell.height !== height || cell.visible !== true) {
- cell.visible = true;
- cell.height = height;
- this.indexDirty = Math.min(this.indexDirty, index);
- this.scheduleUpdate();
- }
- }
-
- private scheduleUpdate() {
- clearTimeout(this.timerUpdate);
- this.timerUpdate = setTimeout(() => this.updateVirtualScroll(), 100);
- }
-
- private updateState() {
- const shouldEnable = !!(this.scrollEl && this.cells);
- if (shouldEnable !== this.isEnabled) {
- this.enableScrollEvents(shouldEnable);
- if (shouldEnable) {
- this.updateVirtualScroll();
- }
- }
- }
-
- private calcCells() {
- if (!this.items) {
- return;
- }
- this.lastItemLen = this.items.length;
- this.cells = calcCells(
- this.items,
- this.itemHeight,
- this.headerHeight,
- this.footerHeight,
- this.headerFn,
- this.footerFn,
- this.approxHeaderHeight,
- this.approxFooterHeight,
- this.approxItemHeight,
- 0,
- 0,
- this.lastItemLen
- );
- this.indexDirty = 0;
- }
-
- private getHeightIndex(): Uint32Array {
- if (this.indexDirty !== Infinity) {
- this.calcHeightIndex(this.indexDirty);
- }
- return this.heightIndex!;
- }
-
- private calcHeightIndex(index = 0) {
- // TODO: optimize, we don't need to calculate all the cells
- this.heightIndex = resizeBuffer(this.heightIndex, this.cells.length);
- this.totalHeight = calcHeightIndex(this.heightIndex, this.cells, index);
-
- this.indexDirty = Infinity;
- }
-
- private enableScrollEvents(shouldListen: boolean) {
- if (this.rmEvent) {
- this.rmEvent();
- this.rmEvent = undefined;
- }
-
- const scrollEl = this.scrollEl;
- if (scrollEl) {
- this.isEnabled = shouldListen;
- scrollEl.addEventListener('scroll', this.onScroll);
- this.rmEvent = () => {
- scrollEl.removeEventListener('scroll', this.onScroll);
- };
- }
- }
-
- private renderVirtualNode(node: VirtualNode) {
- const { type, value, index } = node.cell;
- switch (type) {
- case CELL_TYPE_ITEM:
- return this.renderItem!(value, index);
- case CELL_TYPE_HEADER:
- return this.renderHeader!(value, index);
- case CELL_TYPE_FOOTER:
- return this.renderFooter!(value, index);
- }
- }
-
- render() {
- return (
-
- {this.renderItem && (
-
- {this.virtualDom.map((node) => this.renderVirtualNode(node))}
-
- )}
-
- );
- }
-}
-
-const VirtualProxy: FunctionalComponent<{ dom: VirtualNode[] }> = ({ dom }, children, utils) => {
- return utils.map(children, (child, i) => {
- const node = dom[i];
- const vattrs = child.vattrs || {};
- let classes = vattrs.class || '';
- classes += 'virtual-item ';
- if (!node.visible) {
- classes += 'virtual-loading';
- }
- return {
- ...child,
- vattrs: {
- ...vattrs,
- class: classes,
- style: {
- ...vattrs.style,
- transform: `translate3d(0,${node.top}px,0)`,
- },
- },
- };
- });
-};
diff --git a/core/src/interface.d.ts b/core/src/interface.d.ts
index ad929d324e..99df6ffad1 100644
--- a/core/src/interface.d.ts
+++ b/core/src/interface.d.ts
@@ -35,7 +35,6 @@ export * from './components/tab-bar/tab-bar-interface';
export * from './components/textarea/textarea-interface';
export * from './components/toast/toast-interface';
export * from './components/toggle/toggle-interface';
-export * from './components/virtual-scroll/virtual-scroll-interface';
// Types from utils
export {
diff --git a/core/stencil.config.ts b/core/stencil.config.ts
index 675fae0d86..0c4fd477d6 100644
--- a/core/stencil.config.ts
+++ b/core/stencil.config.ts
@@ -53,7 +53,6 @@ export const config: Config = {
{ components: ['ion-text'] },
{ components: ['ion-toast'] },
{ components: ['ion-toggle'] },
- { components: ['ion-virtual-scroll'] },
{ components: ['ion-accordion-group', 'ion-accordion'] },
{ components: ['ion-breadcrumb', 'ion-breadcrumbs'] },
],
@@ -207,7 +206,6 @@ export const config: Config = {
// auxiliar
'ion-picker-column',
- 'ion-virtual-scroll'
],
/**
* TODO: Abstract custom Ionic value accessor functionality
diff --git a/packages/angular-server/src/ionic-server-module.ts b/packages/angular-server/src/ionic-server-module.ts
index 75b3739a74..51c8932b79 100644
--- a/packages/angular-server/src/ionic-server-module.ts
+++ b/packages/angular-server/src/ionic-server-module.ts
@@ -46,7 +46,6 @@ export function hydrateIonicComponents(doc: any, appId: any) {
// auxiliar
'ion-picker-column',
- 'ion-virtual-scroll',
],
}).then((hydrateResults) => {
hydrateResults.diagnostics.forEach((d) => {
diff --git a/packages/react/README.md b/packages/react/README.md
index 9f77b26e94..83119c9346 100644
--- a/packages/react/README.md
+++ b/packages/react/README.md
@@ -4,14 +4,6 @@ These are React specific building blocks on top of [@ionic/core](https://www.npm
To get started, install the Ionic CLI by running `npm i -g @ionic/cli`. Then, start a new Ionic React Project by running `ionic start myapp --type=react`.
-# Current Status of Components
-
-Below is a list of components yet to be implemented:
-
-| Component |
-| ------------------ |
-| `IonVirtualScroll` |
-
## Publishing a Native Application
You can now make use of all of the ionic components in your React application.
diff --git a/packages/react/src/components/proxies.ts b/packages/react/src/components/proxies.ts
index dd568efaa6..aa2b07fb80 100644
--- a/packages/react/src/components/proxies.ts
+++ b/packages/react/src/components/proxies.ts
@@ -71,7 +71,6 @@ import { defineCustomElement as defineIonThumbnail } from '@ionic/core/component
import { defineCustomElement as defineIonTitle } from '@ionic/core/components/ion-title.js';
import { defineCustomElement as defineIonToggle } from '@ionic/core/components/ion-toggle.js';
import { defineCustomElement as defineIonToolbar } from '@ionic/core/components/ion-toolbar.js';
-import { defineCustomElement as defineIonVirtualScroll } from '@ionic/core/components/ion-virtual-scroll.js';
export const IonAccordion = /*@__PURE__*/createReactComponent('ion-accordion', undefined, undefined, defineIonAccordion);
export const IonAccordionGroup = /*@__PURE__*/createReactComponent('ion-accordion-group', undefined, undefined, defineIonAccordionGroup);
@@ -139,4 +138,3 @@ export const IonThumbnail = /*@__PURE__*/createReactComponent('ion-title', undefined, undefined, defineIonTitle);
export const IonToggle = /*@__PURE__*/createReactComponent('ion-toggle', undefined, undefined, defineIonToggle);
export const IonToolbar = /*@__PURE__*/createReactComponent('ion-toolbar', undefined, undefined, defineIonToolbar);
-export const IonVirtualScroll = /*@__PURE__*/createReactComponent('ion-virtual-scroll', undefined, undefined, defineIonVirtualScroll);
diff --git a/packages/react/src/components/utils/ionRenderToString.ts b/packages/react/src/components/utils/ionRenderToString.ts
index ebc7418773..d68e35d21d 100644
--- a/packages/react/src/components/utils/ionRenderToString.ts
+++ b/packages/react/src/components/utils/ionRenderToString.ts
@@ -32,7 +32,6 @@ export async function ionRenderToString(
// auxiliary
'ion-picker-column',
- 'ion-virtual-scroll',
],
userAgent,
},
diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts
index db99f1e905..538902c489 100644
--- a/packages/vue/src/proxies.ts
+++ b/packages/vue/src/proxies.ts
@@ -76,7 +76,6 @@ import { defineCustomElement as defineIonThumbnail } from '@ionic/core/component
import { defineCustomElement as defineIonTitle } from '@ionic/core/components/ion-title.js';
import { defineCustomElement as defineIonToggle } from '@ionic/core/components/ion-toggle.js';
import { defineCustomElement as defineIonToolbar } from '@ionic/core/components/ion-toolbar.js';
-import { defineCustomElement as defineIonVirtualScroll } from '@ionic/core/components/ion-virtual-scroll.js';
export const IonAccordion = /*@__PURE__*/ defineContainer('ion-accordion', defineIonAccordion, [
@@ -841,21 +840,3 @@ export const IonToolbar = /*@__PURE__*/ defineContainer('ion-too
'color'
]);
-
-export const IonVirtualScroll = /*@__PURE__*/ defineContainer('ion-virtual-scroll', defineIonVirtualScroll, [
- 'approxItemHeight',
- 'approxHeaderHeight',
- 'approxFooterHeight',
- 'headerFn',
- 'footerFn',
- 'items',
- 'itemHeight',
- 'headerHeight',
- 'footerHeight',
- 'renderItem',
- 'renderHeader',
- 'renderFooter',
- 'nodeRender',
- 'domRender'
-]);
-
diff --git a/packages/vue/types/proxies.d.ts b/packages/vue/types/proxies.d.ts
index 216d86faeb..6c199f708e 100644
--- a/packages/vue/types/proxies.d.ts
+++ b/packages/vue/types/proxies.d.ts
@@ -84,5 +84,4 @@ export declare const IonTitle: import("vue").FunctionalComponent;
export declare const IonToggle: import("vue").FunctionalComponent;
export declare const IonToolbar: import("vue").FunctionalComponent;
-export declare const IonVirtualScroll: import("vue").FunctionalComponent;
//# sourceMappingURL=proxies.d.ts.map