refactor(all): avoid using export const enum (#16614)

* refactor(all): avoid using export const enum

fixes #16497

* add tslint
This commit is contained in:
Manu MA
2018-12-06 21:19:49 +01:00
committed by GitHub
parent 95c0b1bac7
commit 119e0c1fd2
24 changed files with 150 additions and 166 deletions

View File

@@ -0,0 +1,5 @@
export const LIFECYCLE_WILL_ENTER = 'ionViewWillEnter';
export const LIFECYCLE_DID_ENTER = 'ionViewDidEnter';
export const LIFECYCLE_WILL_LEAVE = 'ionViewWillLeave';
export const LIFECYCLE_DID_LEAVE = 'ionViewDidLeave';
export const LIFECYCLE_WILL_UNLOAD = 'ionViewWillUnload';

View File

@@ -1,4 +1,6 @@
import { Animation, AnimationBuilder, ComponentRef, FrameworkDelegate, Mode, ViewController } from '../../interface';
import { Animation, AnimationBuilder, ComponentRef, FrameworkDelegate, Mode } from '../../interface';
import { ViewController } from './view-controller';
export type NavDirection = 'back' | 'forward';
@@ -62,3 +64,5 @@ export interface TransitionInstruction {
leavingRequiresTransition?: boolean;
enteringRequiresTransition?: boolean;
}
export { ViewController };

View File

@@ -1,11 +1,11 @@
import { Build, Component, Element, Event, EventEmitter, Method, Prop, QueueApi, Watch } from '@stencil/core';
import { ViewLifecycle } from '../..';
import { Animation, AnimationBuilder, ComponentProps, Config, FrameworkDelegate, Gesture, Mode, NavComponent, NavOptions, NavOutlet, NavResult, RouteID, RouteWrite, TransitionDoneFn, TransitionInstruction, ViewController } from '../../interface';
import { Animation, AnimationBuilder, ComponentProps, Config, FrameworkDelegate, Gesture, Mode, NavComponent, NavOptions, NavOutlet, NavResult, RouteID, RouteWrite, RouterDirection, TransitionDoneFn, TransitionInstruction, ViewController } from '../../interface';
import { assert } from '../../utils/helpers';
import { TransitionOptions, lifecycle, setPageHidden, transition } from '../../utils/transition';
import { ViewState, convertToViews, matches } from './view-controller';
import { LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_LEAVE, LIFECYCLE_WILL_UNLOAD } from './constants';
import { VIEW_STATE_ATTACHED, VIEW_STATE_DESTROYED, VIEW_STATE_NEW, convertToViews, matches } from './view-controller';
@Component({
tag: 'ion-nav',
@@ -124,7 +124,7 @@ export class Nav implements NavOutlet {
componentDidUnload() {
for (const view of this.views) {
lifecycle(view.element!, ViewLifecycle.WillUnload);
lifecycle(view.element!, LIFECYCLE_WILL_UNLOAD);
view._destroy();
}
@@ -324,7 +324,7 @@ export class Nav implements NavOutlet {
setRouteId(
id: string,
params: ComponentProps | undefined,
direction: number
direction: RouterDirection
): Promise<RouteWrite> {
const active = this.getActiveSync();
if (matches(active, id, params)) {
@@ -354,7 +354,7 @@ export class Nav implements NavOutlet {
}
};
if (direction === 0) {
if (direction === 'root') {
finish = this.setRoot(id, params, commonOpts);
} else {
const viewController = this.views.find(v => matches(v, id, params));
@@ -364,9 +364,9 @@ export class Nav implements NavOutlet {
...commonOpts,
direction: 'back'
});
} else if (direction === 1) {
} else if (direction === 'forward') {
finish = this.push(id, params, commonOpts);
} else if (direction === -1) {
} else if (direction === 'back') {
finish = this.setRoot(id, params, {
...commonOpts,
direction: 'back',
@@ -502,7 +502,7 @@ export class Nav implements NavOutlet {
if (ti.opts!.updateURL !== false && this.useRouter) {
const router = this.win.document.querySelector('ion-router');
if (router) {
const direction = result.direction === 'back' ? -1 : 1;
const direction = result.direction === 'back' ? 'back' : 'forward';
router.navChanged(direction);
}
}
@@ -560,7 +560,7 @@ export class Nav implements NavOutlet {
throw new Error('no views in the stack to be removed');
}
if (enteringView && enteringView.state === ViewState.New) {
if (enteringView && enteringView.state === VIEW_STATE_NEW) {
await enteringView.init(this.el);
}
this.postViewInit(enteringView, leavingView, ti);
@@ -645,7 +645,7 @@ export class Nav implements NavOutlet {
if (nav && nav !== this) {
throw new Error('inserted view was already inserted');
}
if (view.state === ViewState.Destroyed) {
if (view.state === VIEW_STATE_DESTROYED) {
throw new Error('inserted view was already destroyed');
}
}
@@ -749,9 +749,9 @@ export class Nav implements NavOutlet {
// let's make sure, callbacks are zoned
if (destroyQueue && destroyQueue.length > 0) {
for (const view of destroyQueue) {
lifecycle(view.element, ViewLifecycle.WillLeave);
lifecycle(view.element, ViewLifecycle.DidLeave);
lifecycle(view.element, ViewLifecycle.WillUnload);
lifecycle(view.element, LIFECYCLE_WILL_LEAVE);
lifecycle(view.element, LIFECYCLE_DID_LEAVE);
lifecycle(view.element, LIFECYCLE_WILL_UNLOAD);
}
// once all lifecycle events has been delivered, we can safely detroy the views
@@ -837,7 +837,7 @@ export class Nav implements NavOutlet {
private removeView(view: ViewController) {
assert(
view.state === ViewState.Attached || view.state === ViewState.Destroyed,
view.state === VIEW_STATE_ATTACHED || view.state === VIEW_STATE_DESTROYED,
'view state should be loaded or destroyed'
);
@@ -873,7 +873,7 @@ export class Nav implements NavOutlet {
if (i > activeViewIndex) {
// this view comes after the active view
// let's unload it
lifecycle(element, ViewLifecycle.WillUnload);
lifecycle(element, LIFECYCLE_WILL_UNLOAD);
this.destroyView(view);
} else if (i < activeViewIndex) {
// this view comes before the active view

View File

@@ -952,7 +952,7 @@ describe('NavController', () => {
? win.document.createElement(enteringView.component)
: enteringView.element = enteringView.component as HTMLElement;
}
enteringView.state = ViewState.Attached;
enteringView.state = VIEW_STATE_ATTACHED;
};
return navI;
}

View File

@@ -2,15 +2,13 @@ import { ComponentProps, FrameworkDelegate } from '../../interface';
import { attachComponent } from '../../utils/framework-delegate';
import { assert } from '../../utils/helpers';
export const enum ViewState {
New = 1,
Attached,
Destroyed
}
export const VIEW_STATE_NEW = 1;
export const VIEW_STATE_ATTACHED = 2;
export const VIEW_STATE_DESTROYED = 3;
export class ViewController {
state: ViewState = ViewState.New;
state = VIEW_STATE_NEW;
nav?: any;
element?: HTMLElement;
delegate?: FrameworkDelegate;
@@ -21,7 +19,7 @@ export class ViewController {
) {}
async init(container: HTMLElement) {
this.state = ViewState.Attached;
this.state = VIEW_STATE_ATTACHED;
if (!this.element) {
const component = this.component;
@@ -33,7 +31,7 @@ export class ViewController {
* DOM WRITE
*/
_destroy() {
assert(this.state !== ViewState.Destroyed, 'view state must be ATTACHED');
assert(this.state !== VIEW_STATE_DESTROYED, 'view state must be ATTACHED');
const element = this.element;
if (element) {
@@ -44,7 +42,7 @@ export class ViewController {
}
}
this.nav = undefined;
this.state = ViewState.Destroyed;
this.state = VIEW_STATE_DESTROYED;
}
}

View File

@@ -1,6 +1,6 @@
import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Prop, QueueApi, Watch } from '@stencil/core';
import { Animation, AnimationBuilder, ComponentProps, ComponentRef, Config, FrameworkDelegate, Gesture, Mode, NavOutlet, RouteID, RouteWrite, RouterOutletOptions, SwipeGestureHandler } from '../../interface';
import { Animation, AnimationBuilder, ComponentProps, ComponentRef, Config, FrameworkDelegate, Gesture, Mode, NavOutlet, RouteID, RouteWrite, RouterDirection, RouterOutletOptions, SwipeGestureHandler } from '../../interface';
import { transition } from '../../utils';
import { attachComponent, detachComponent } from '../../utils/framework-delegate';
@@ -102,10 +102,10 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
/** @internal */
@Method()
async setRouteId(id: string, params: ComponentProps | undefined, direction: number): Promise<RouteWrite> {
async setRouteId(id: string, params: ComponentProps | undefined, direction: RouterDirection): Promise<RouteWrite> {
const changed = await this.setRoot(id, params, {
duration: direction === 0 ? 0 : undefined,
direction: direction === -1 ? 'back' : 'forward',
duration: direction === 'root' ? 0 : undefined,
direction: direction === 'back' ? 'back' : 'forward',
});
return {
changed,

View File

@@ -3,7 +3,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Listen, Me
import { BackButtonEvent, Config, RouteChain, RouterDirection, RouterEventDetail } from '../../interface';
import { debounce } from '../../utils/helpers';
import { RouterIntent } from './utils/constants';
import { ROUTER_INTENT_BACK, ROUTER_INTENT_FORWARD, ROUTER_INTENT_NONE } from './utils/constants';
import { printRedirects, printRoutes } from './utils/debug';
import { readNavState, waitUntilNavNode, writeNavState } from './utils/dom';
import { routeRedirect, routerIDsToChain, routerPathToChain } from './utils/matching';
@@ -97,9 +97,8 @@ export class Router implements ComponentInterface {
console.debug('[ion-router] URL pushed -> updating nav', url, direction);
const path = parsePath(url);
const intent = DIRECTION_TO_INTENT[direction];
this.setPath(path, intent);
return this.writeNavStateRoot(path, intent);
this.setPath(path, direction);
return this.writeNavStateRoot(path, direction);
}
/**
@@ -122,7 +121,7 @@ export class Router implements ComponentInterface {
/** @internal */
@Method()
async navChanged(intent: number): Promise<boolean> {
async navChanged(direction: RouterDirection): Promise<boolean> {
if (this.busy) {
console.warn('[ion-router] router is busy, navChanged was cancelled');
return false;
@@ -142,21 +141,21 @@ export class Router implements ComponentInterface {
}
console.debug('[ion-router] nav changed -> update URL', ids, path);
this.setPath(path, intent);
this.setPath(path, direction);
await this.safeWriteNavState(outlet, chain, RouterIntent.None, path, null, ids.length);
await this.safeWriteNavState(outlet, chain, ROUTER_INTENT_NONE, path, null, ids.length);
return true;
}
private onRedirectChanged() {
const path = this.getPath();
if (path && routeRedirect(path, readRedirects(this.el))) {
this.writeNavStateRoot(path, RouterIntent.None);
this.writeNavStateRoot(path, ROUTER_INTENT_NONE);
}
}
private onRoutesChanged() {
return this.writeNavStateRoot(this.getPath(), RouterIntent.None);
return this.writeNavStateRoot(this.getPath(), ROUTER_INTENT_NONE);
}
private historyDirection() {
@@ -172,15 +171,15 @@ export class Router implements ComponentInterface {
this.lastState = state;
if (state > lastState) {
return RouterIntent.Forward;
return ROUTER_INTENT_FORWARD;
} else if (state < lastState) {
return RouterIntent.Back;
return ROUTER_INTENT_BACK;
} else {
return RouterIntent.None;
return ROUTER_INTENT_NONE;
}
}
private async writeNavStateRoot(path: string[] | null, intent: RouterIntent): Promise<boolean> {
private async writeNavStateRoot(path: string[] | null, direction: RouterDirection): Promise<boolean> {
if (!path) {
console.error('[ion-router] URL is not part of the routing set');
return false;
@@ -191,7 +190,7 @@ export class Router implements ComponentInterface {
const redirect = routeRedirect(path, redirects);
let redirectFrom: string[] | null = null;
if (redirect) {
this.setPath(redirect.to!, intent);
this.setPath(redirect.to!, direction);
redirectFrom = redirect.from;
path = redirect.to!;
}
@@ -205,18 +204,18 @@ export class Router implements ComponentInterface {
}
// write DOM give
return this.safeWriteNavState(this.win.document.body, chain, intent, path, redirectFrom);
return this.safeWriteNavState(this.win.document.body, chain, direction, path, redirectFrom);
}
private async safeWriteNavState(
node: HTMLElement | undefined, chain: RouteChain, intent: RouterIntent,
node: HTMLElement | undefined, chain: RouteChain, direction: RouterDirection,
path: string[], redirectFrom: string[] | null,
index = 0
): Promise<boolean> {
const unlock = await this.lock();
let changed = false;
try {
changed = await this.writeNavState(node, chain, intent, path, redirectFrom, index);
changed = await this.writeNavState(node, chain, direction, path, redirectFrom, index);
} catch (e) {
console.error(e);
}
@@ -236,7 +235,7 @@ export class Router implements ComponentInterface {
}
private async writeNavState(
node: HTMLElement | undefined, chain: RouteChain, intent: RouterIntent,
node: HTMLElement | undefined, chain: RouteChain, direction: RouterDirection,
path: string[], redirectFrom: string[] | null,
index = 0
): Promise<boolean> {
@@ -252,7 +251,7 @@ export class Router implements ComponentInterface {
this.ionRouteWillChange.emit(routeEvent);
}
const changed = await writeNavState(node, chain, intent, index);
const changed = await writeNavState(node, chain, direction, index);
this.busy = false;
if (changed) {
@@ -266,9 +265,9 @@ export class Router implements ComponentInterface {
return changed;
}
private setPath(path: string[], intent: RouterIntent) {
private setPath(path: string[], direction: RouterDirection) {
this.state++;
writePath(this.win.history, this.root, this.useHash, path, intent, this.state);
writePath(this.win.history, this.root, this.useHash, path, direction, this.state);
}
private getPath(): string[] | null {
@@ -290,9 +289,3 @@ export class Router implements ComponentInterface {
};
}
}
const DIRECTION_TO_INTENT = {
'back': RouterIntent.Back,
'root': RouterIntent.None,
'forward': RouterIntent.Forward
};

View File

@@ -1,4 +1,4 @@
import { RouterIntent } from '../utils/constants';
import { ROUTER_INTENT_FORWARD } from '../utils/constants';
import { RouteChain } from '../utils/interface';
import { chainToPath, generatePath, parsePath, readPath, writePath } from '../utils/path';
@@ -178,61 +178,61 @@ describe('readPath', () => {
describe('writePath', () => {
it('should write root path (no hash)', () => {
const history = mockHistory();
writePath(history, '', false, [''], RouterIntent.Forward, 123);
writePath(history, '', false, [''], ROUTER_INTENT_FORWARD, 123);
expect(history.pushState).toHaveBeenCalledWith(123, '', '/');
writePath(history, '', false, ['schedule'], RouterIntent.Forward, 123);
writePath(history, '', false, ['schedule'], ROUTER_INTENT_FORWARD, 123);
expect(history.pushState).toHaveBeenCalledWith(123, '', '/schedule');
writePath(history, '/', false, [''], RouterIntent.Forward, 123);
writePath(history, '/', false, [''], ROUTER_INTENT_FORWARD, 123);
expect(history.pushState).toHaveBeenCalledWith(123, '', '/');
writePath(history, '/', false, ['to', 'schedule'], RouterIntent.Forward, 123);
writePath(history, '/', false, ['to', 'schedule'], ROUTER_INTENT_FORWARD, 123);
expect(history.pushState).toHaveBeenCalledWith(123, '', '/to/schedule');
});
it('should write non root path (no hash)', () => {
const history = mockHistory();
writePath(history, '/path', false, [''], RouterIntent.Forward, 2);
writePath(history, '/path', false, [''], ROUTER_INTENT_FORWARD, 2);
expect(history.pushState).toHaveBeenCalledWith(2, '', '/path');
writePath(history, '/path', false, ['to', 'page'], RouterIntent.Forward, 2);
writePath(history, '/path', false, ['to', 'page'], ROUTER_INTENT_FORWARD, 2);
expect(history.pushState).toHaveBeenCalledWith(2, '', '/path/to/page');
writePath(history, 'path/to', false, ['second', 'page'], RouterIntent.Forward, 2);
writePath(history, 'path/to', false, ['second', 'page'], ROUTER_INTENT_FORWARD, 2);
expect(history.pushState).toHaveBeenCalledWith(2, '', '/path/to/second/page');
writePath(history, '/path/to/', false, ['second', 'page'], RouterIntent.Forward, 2);
writePath(history, '/path/to/', false, ['second', 'page'], ROUTER_INTENT_FORWARD, 2);
expect(history.pushState).toHaveBeenCalledWith(2, '', '/path/to/second/page');
});
it('should write root path (no hash)', () => {
const history = mockHistory();
writePath(history, '', true, [''], RouterIntent.Forward, 123);
writePath(history, '', true, [''], ROUTER_INTENT_FORWARD, 123);
expect(history.pushState).toHaveBeenCalledWith(123, '', '#/');
writePath(history, '', true, ['schedule'], RouterIntent.Forward, 123);
writePath(history, '', true, ['schedule'], ROUTER_INTENT_FORWARD, 123);
expect(history.pushState).toHaveBeenCalledWith(123, '', '#/schedule');
writePath(history, '/', true, [''], RouterIntent.Forward, 123);
writePath(history, '/', true, [''], ROUTER_INTENT_FORWARD, 123);
expect(history.pushState).toHaveBeenCalledWith(123, '', '#/');
writePath(history, '/', true, ['to', 'schedule'], RouterIntent.Forward, 123);
writePath(history, '/', true, ['to', 'schedule'], ROUTER_INTENT_FORWARD, 123);
expect(history.pushState).toHaveBeenCalledWith(123, '', '#/to/schedule');
});
it('should write non root path (no hash)', () => {
const history = mockHistory();
writePath(history, '/path', true, [''], RouterIntent.Forward, 123);
writePath(history, '/path', true, [''], ROUTER_INTENT_FORWARD, 123);
expect(history.pushState).toHaveBeenCalledWith(123, '', '#/path');
writePath(history, '/path', true, ['to', 'page'], RouterIntent.Forward, 123);
writePath(history, '/path', true, ['to', 'page'], ROUTER_INTENT_FORWARD, 123);
expect(history.pushState).toHaveBeenCalledWith(123, '', '#/path/to/page');
writePath(history, 'path/to', true, ['second', 'page'], RouterIntent.Forward, 123);
writePath(history, 'path/to', true, ['second', 'page'], ROUTER_INTENT_FORWARD, 123);
expect(history.pushState).toHaveBeenCalledWith(123, '', '#/path/to/second/page');
writePath(history, '/path/to/', true, ['second', 'page'], RouterIntent.Forward, 123);
writePath(history, '/path/to/', true, ['second', 'page'], ROUTER_INTENT_FORWARD, 123);
expect(history.pushState).toHaveBeenCalledWith(123, '', '#/path/to/second/page');
});
});

View File

@@ -1,6 +1,4 @@
export const enum RouterIntent {
None = 0,
Forward = 1,
Back = -1,
}
export const ROUTER_INTENT_NONE = 'root';
export const ROUTER_INTENT_FORWARD = 'forward';
export const ROUTER_INTENT_BACK = 'back';

View File

@@ -1,11 +1,11 @@
import { NavOutletElement, RouteChain, RouteID } from '../../../interface';
import { NavOutletElement, RouteChain, RouteID, RouterDirection } from '../../../interface';
import { RouterIntent } from './constants';
import { ROUTER_INTENT_NONE } from './constants';
export async function writeNavState(
root: HTMLElement | undefined,
chain: RouteChain,
intent: RouterIntent,
direction: RouterDirection,
index: number,
changed = false
): Promise<boolean> {
@@ -20,17 +20,17 @@ export async function writeNavState(
await outlet.componentOnReady();
const route = chain[index];
const result = await outlet.setRouteId(route.id, route.params, intent);
const result = await outlet.setRouteId(route.id, route.params, direction);
// if the outlet changed the page, reset navigation to neutral (no direction)
// this means nested outlets will not animate
if (result.changed) {
intent = RouterIntent.None;
direction = ROUTER_INTENT_NONE;
changed = true;
}
// recursively set nested outlets
changed = await writeNavState(result.element, chain, intent, index + 1, changed);
changed = await writeNavState(result.element, chain, direction, index + 1, changed);
// once all nested outlets are visible let's make the parent visible too,
// using markVisible prevents flickering

View File

@@ -1,7 +1,7 @@
import { ComponentProps } from '../../../interface';
export interface NavOutlet {
setRouteId(id: string, params: ComponentProps | undefined, direction: number): Promise<RouteWrite>;
setRouteId(id: string, params: ComponentProps | undefined, direction: RouterDirection): Promise<RouteWrite>;
getRouteId(): Promise<RouteID | undefined>;
}

View File

@@ -1,6 +1,6 @@
import { RouteChain } from '../../../interface';
import { RouteChain, RouterDirection } from '../../../interface';
import { RouterIntent } from './constants';
import { ROUTER_INTENT_FORWARD } from './constants';
export function generatePath(segments: string[]): string {
const path = segments
@@ -28,7 +28,7 @@ export function chainToPath(chain: RouteChain): string[] | null {
return path;
}
export function writePath(history: History, root: string, useHash: boolean, path: string[], intent: RouterIntent, state: number) {
export function writePath(history: History, root: string, useHash: boolean, path: string[], direction: RouterDirection, state: number) {
let url = generatePath([
...parsePath(root),
...path
@@ -36,7 +36,7 @@ export function writePath(history: History, root: string, useHash: boolean, path
if (useHash) {
url = '#' + url;
}
if (intent === RouterIntent.Forward) {
if (direction === ROUTER_INTENT_FORWARD) {
history.pushState(state, '', url);
} else {
history.replaceState(state, '', url);

View File

@@ -187,7 +187,7 @@ export class Tabs implements NavOutlet {
if (this.useRouter) {
const router = this.doc.querySelector('ion-router');
if (router) {
return router.navChanged(1);
return router.navChanged('forward');
}
}
return Promise.resolve(false);

View File

@@ -0,0 +1,7 @@
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;

View File

@@ -1,6 +1,6 @@
import { HeaderFn, ItemHeightFn, VirtualNode } from '../../../interface';
import { CellType } from '../virtual-scroll-interface';
import { Range, calcCells, calcHeightIndex, getRange, getShouldUpdate, getViewport, positionForIndex, resizeBuffer, updateVDom } from '../virtual-scroll-utils';
import { CELL_TYPE_ITEM, CELL_TYPE_HEADER, CELL_TYPE_FOOTER } from '../constants';
describe('getViewport', () => {
it('should return viewport without margin', () => {
@@ -149,7 +149,7 @@ describe('calcCells', () => {
const cells = calcCells(items, undefined, undefined, undefined, 10, 20, 30, 0, 0, items.length);
expect(cells).toEqual([
{
type: CellType.Item,
type: CELL_TYPE_ITEM,
value: '0',
i: 0,
index: 0,
@@ -158,7 +158,7 @@ describe('calcCells', () => {
visible: false,
},
{
type: CellType.Item,
type: CELL_TYPE_ITEM,
value: 2,
i: 1,
index: 1,
@@ -167,7 +167,7 @@ describe('calcCells', () => {
visible: false,
},
{
type: CellType.Item,
type: CELL_TYPE_ITEM,
value: 'hola',
i: 2,
index: 2,
@@ -176,7 +176,7 @@ describe('calcCells', () => {
visible: false,
},
{
type: CellType.Item,
type: CELL_TYPE_ITEM,
value: { data: 'hello' },
i: 3,
index: 3,
@@ -200,7 +200,7 @@ describe('calcCells', () => {
expect(called).toEqual(3);
expect(cells).toEqual([
{
type: CellType.Item,
type: CELL_TYPE_ITEM,
value: 10,
i: 0,
index: 0,
@@ -209,7 +209,7 @@ describe('calcCells', () => {
visible: true,
},
{
type: CellType.Item,
type: CELL_TYPE_ITEM,
value: 9,
i: 1,
index: 1,
@@ -218,7 +218,7 @@ describe('calcCells', () => {
visible: true,
},
{
type: CellType.Item,
type: CELL_TYPE_ITEM,
value: 8,
i: 2,
index: 2,
@@ -258,7 +258,7 @@ describe('calcCells', () => {
expect(footerCalled).toEqual(3);
expect(cells).toEqual([
{
type: CellType.Header,
type: CELL_TYPE_HEADER,
value: 'my header',
i: 0,
index: 0,
@@ -267,7 +267,7 @@ describe('calcCells', () => {
visible: false,
},
{
type: CellType.Item,
type: CELL_TYPE_ITEM,
value: '10',
i: 1,
index: 0,
@@ -276,7 +276,7 @@ describe('calcCells', () => {
visible: true,
},
{
type: CellType.Item,
type: CELL_TYPE_ITEM,
value: '9',
i: 2,
index: 1,
@@ -285,7 +285,7 @@ describe('calcCells', () => {
visible: true,
},
{
type: CellType.Item,
type: CELL_TYPE_ITEM,
value: '8',
i: 3,
index: 2,
@@ -294,7 +294,7 @@ describe('calcCells', () => {
visible: true,
},
{
type: CellType.Footer,
type: CELL_TYPE_FOOTER,
value: 'my footer',
i: 4,
index: 2,

View File

@@ -1,16 +1,4 @@
export const enum CellType {
Item,
Header,
Footer
}
export const enum NodeChange {
NoChange,
Position,
Cell,
}
export interface Cell {
i: number;
index: number;
@@ -29,6 +17,8 @@ export interface VirtualNode {
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 ItemRenderFn = (el: HTMLElement | null, cell: Cell, domIndex: number) => HTMLElement;

View File

@@ -1,6 +1,7 @@
import { Cell, HeaderFn, ItemHeightFn, ItemRenderFn, VirtualNode } from '../../interface';
import { CellType, NodeChange } from './virtual-scroll-interface';
import { CELL_TYPE_FOOTER, CELL_TYPE_HEADER, CELL_TYPE_ITEM, NODE_CHANGE_CELL, NODE_CHANGE_NONE, NODE_CHANGE_POSITION } from './constants';
import { CellType } from './virtual-scroll-interface';
export interface Viewport {
top: number;
@@ -17,7 +18,7 @@ const MIN_READS = 2;
export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells: Cell[], range: Range) {
// reset dom
for (const node of dom) {
node.change = NodeChange.NoChange;
node.change = NODE_CHANGE_NONE;
node.d = true;
}
@@ -32,7 +33,7 @@ export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells:
const top = heightIndex[i];
if (top !== node.top) {
node.top = top;
node.change = NodeChange.Position;
node.change = NODE_CHANGE_POSITION;
}
node.d = false;
} else {
@@ -48,7 +49,7 @@ export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells:
const index = cell.index;
if (node) {
node.d = false;
node.change = NodeChange.Cell;
node.change = NODE_CHANGE_CELL;
node.cell = cell;
node.top = heightIndex[index];
} else {
@@ -56,7 +57,7 @@ export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells:
d: false,
cell,
visible: true,
change: NodeChange.Cell,
change: NODE_CHANGE_CELL,
top: heightIndex[index],
});
}
@@ -64,7 +65,7 @@ export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells:
dom
.filter(n => n.d && n.top !== -9999)
.forEach(n => {
n.change = NodeChange.Position;
n.change = NODE_CHANGE_POSITION;
n.top = -9999;
});
}
@@ -83,7 +84,7 @@ export function doRender(
const cell = node.cell;
// the cell change, the content must be updated
if (node.change === NodeChange.Cell) {
if (node.change === NODE_CHANGE_CELL) {
if (i < childrenNu) {
child = children[i] as HTMLElement;
nodeRender(child, cell, i);
@@ -99,7 +100,7 @@ export function doRender(
}
// only update position when it changes
if (node.change !== NodeChange.NoChange) {
if (node.change !== NODE_CHANGE_NONE) {
child.style.transform = `translate3d(0,${node.top}px,0)`;
}
@@ -132,9 +133,9 @@ function createNode(el: HTMLElement, type: CellType): HTMLElement | null {
function getTemplate(el: HTMLElement, type: CellType): HTMLTemplateElement | null {
switch (type) {
case CellType.Item: return el.querySelector('template:not([name])');
case CellType.Header: return el.querySelector('template[name=header]');
case CellType.Footer: return el.querySelector('template[name=footer]');
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]');
}
}
@@ -220,7 +221,7 @@ export function calcCells(
if (value != null) {
cells.push({
i: j++,
type: CellType.Header,
type: CELL_TYPE_HEADER,
value,
index: i,
height: approxHeaderHeight,
@@ -232,7 +233,7 @@ export function calcCells(
cells.push({
i: j++,
type: CellType.Item,
type: CELL_TYPE_ITEM,
value: item,
index: i,
height: itemHeight ? itemHeight(item, i) : approxItemHeight,
@@ -245,7 +246,7 @@ export function calcCells(
if (value != null) {
cells.push({
i: j++,
type: CellType.Footer,
type: CELL_TYPE_FOOTER,
value,
index: i,
height: approxFooterHeight,
@@ -283,7 +284,7 @@ export function resizeBuffer(buf: Uint32Array | undefined, len: number) {
}
export function positionForIndex(index: number, cells: Cell[], heightIndex: Uint32Array): number {
const cell = cells.find(c => c.type === CellType.Item && c.index === index);
const cell = cells.find(c => c.type === CELL_TYPE_ITEM && c.index === index);
if (cell) {
return heightIndex[cell.i];
}

View File

@@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, EventListenerEnable, Functional
import { Cell, DomRenderFn, HeaderFn, ItemHeightFn, ItemRenderFn, VirtualNode } from '../../interface';
import { CellType } from './virtual-scroll-interface';
import { CELL_TYPE_FOOTER, CELL_TYPE_HEADER, CELL_TYPE_ITEM } from './constants';
import { Range, calcCells, calcHeightIndex, doRender, findCellIndex, getRange, getShouldUpdate, getViewport, inplaceUpdate, positionForIndex, resizeBuffer, updateVDom } from './virtual-scroll-utils';
@Component({
@@ -411,9 +411,9 @@ export class VirtualScroll implements ComponentInterface {
private renderVirtualNode(node: VirtualNode) {
const { type, value, index } = node.cell;
switch (type) {
case CellType.Item: return this.renderItem!(value, index);
case CellType.Header: return this.renderHeader!(value, index);
case CellType.Footer: return this.renderFooter!(value, index);
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);
}
}