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

@ -53,7 +53,7 @@
"stylelint-order": "~1.0.0", "stylelint-order": "~1.0.0",
"swiper": "4.4.1", "swiper": "4.4.1",
"tslint": "^5.10.0", "tslint": "^5.10.0",
"tslint-ionic-rules": "0.0.20", "tslint-ionic-rules": "0.0.21",
"tslint-react": "^3.6.0", "tslint-react": "^3.6.0",
"typescript": "^2.9.2" "typescript": "^2.9.2"
}, },

View File

@ -2795,7 +2795,7 @@ export namespace Components {
* Set the root for the current navigation stack. * Set the root for the current navigation stack.
*/ */
'setRoot': <T extends NavComponent>(component: T, componentProps?: ComponentProps<T> | null | undefined, opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>; 'setRoot': <T extends NavComponent>(component: T, componentProps?: ComponentProps<T> | null | undefined, opts?: NavOptions | null | undefined, done?: TransitionDoneFn | undefined) => Promise<boolean>;
'setRouteId': (id: string, params: { [key: string]: any; } | undefined, direction: number) => Promise<RouteWrite>; 'setRouteId': (id: string, params: { [key: string]: any; } | undefined, direction: RouterDirection) => Promise<RouteWrite>;
/** /**
* If the nav component should allow for swipe-to-go-back. * If the nav component should allow for swipe-to-go-back.
*/ */
@ -3626,7 +3626,7 @@ export namespace Components {
'delegate'?: FrameworkDelegate; 'delegate'?: FrameworkDelegate;
'getRouteId': () => Promise<RouteID | undefined>; 'getRouteId': () => Promise<RouteID | undefined>;
'mode': Mode; 'mode': Mode;
'setRouteId': (id: string, params: { [key: string]: any; } | undefined, direction: number) => Promise<RouteWrite>; 'setRouteId': (id: string, params: { [key: string]: any; } | undefined, direction: RouterDirection) => Promise<RouteWrite>;
'swipeHandler'?: SwipeGestureHandler; 'swipeHandler'?: SwipeGestureHandler;
} }
interface IonRouterOutletAttributes extends StencilHTMLAttributes { interface IonRouterOutletAttributes extends StencilHTMLAttributes {
@ -3651,7 +3651,7 @@ export namespace Components {
* Go back to previous page in the window.history. * Go back to previous page in the window.history.
*/ */
'goBack': () => Promise<void>; 'goBack': () => Promise<void>;
'navChanged': (intent: number) => Promise<boolean>; 'navChanged': (direction: RouterDirection) => Promise<boolean>;
'printDebug': () => void; 'printDebug': () => void;
/** /**
* Navigate to the specified URL. * Navigate to the specified URL.

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

View File

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

View File

@ -2,15 +2,13 @@ import { ComponentProps, FrameworkDelegate } from '../../interface';
import { attachComponent } from '../../utils/framework-delegate'; import { attachComponent } from '../../utils/framework-delegate';
import { assert } from '../../utils/helpers'; import { assert } from '../../utils/helpers';
export const enum ViewState { export const VIEW_STATE_NEW = 1;
New = 1, export const VIEW_STATE_ATTACHED = 2;
Attached, export const VIEW_STATE_DESTROYED = 3;
Destroyed
}
export class ViewController { export class ViewController {
state: ViewState = ViewState.New; state = VIEW_STATE_NEW;
nav?: any; nav?: any;
element?: HTMLElement; element?: HTMLElement;
delegate?: FrameworkDelegate; delegate?: FrameworkDelegate;
@ -21,7 +19,7 @@ export class ViewController {
) {} ) {}
async init(container: HTMLElement) { async init(container: HTMLElement) {
this.state = ViewState.Attached; this.state = VIEW_STATE_ATTACHED;
if (!this.element) { if (!this.element) {
const component = this.component; const component = this.component;
@ -33,7 +31,7 @@ export class ViewController {
* DOM WRITE * DOM WRITE
*/ */
_destroy() { _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; const element = this.element;
if (element) { if (element) {
@ -44,7 +42,7 @@ export class ViewController {
} }
} }
this.nav = undefined; 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 { 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 { transition } from '../../utils';
import { attachComponent, detachComponent } from '../../utils/framework-delegate'; import { attachComponent, detachComponent } from '../../utils/framework-delegate';
@ -102,10 +102,10 @@ export class RouterOutlet implements ComponentInterface, NavOutlet {
/** @internal */ /** @internal */
@Method() @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, { const changed = await this.setRoot(id, params, {
duration: direction === 0 ? 0 : undefined, duration: direction === 'root' ? 0 : undefined,
direction: direction === -1 ? 'back' : 'forward', direction: direction === 'back' ? 'back' : 'forward',
}); });
return { return {
changed, changed,

View File

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

View File

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

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

View File

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

View File

@ -187,7 +187,7 @@ export class Tabs implements NavOutlet {
if (this.useRouter) { if (this.useRouter) {
const router = this.doc.querySelector('ion-router'); const router = this.doc.querySelector('ion-router');
if (router) { if (router) {
return router.navChanged(1); return router.navChanged('forward');
} }
} }
return Promise.resolve(false); 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 { 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 { 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', () => { describe('getViewport', () => {
it('should return viewport without margin', () => { 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); const cells = calcCells(items, undefined, undefined, undefined, 10, 20, 30, 0, 0, items.length);
expect(cells).toEqual([ expect(cells).toEqual([
{ {
type: CellType.Item, type: CELL_TYPE_ITEM,
value: '0', value: '0',
i: 0, i: 0,
index: 0, index: 0,
@ -158,7 +158,7 @@ describe('calcCells', () => {
visible: false, visible: false,
}, },
{ {
type: CellType.Item, type: CELL_TYPE_ITEM,
value: 2, value: 2,
i: 1, i: 1,
index: 1, index: 1,
@ -167,7 +167,7 @@ describe('calcCells', () => {
visible: false, visible: false,
}, },
{ {
type: CellType.Item, type: CELL_TYPE_ITEM,
value: 'hola', value: 'hola',
i: 2, i: 2,
index: 2, index: 2,
@ -176,7 +176,7 @@ describe('calcCells', () => {
visible: false, visible: false,
}, },
{ {
type: CellType.Item, type: CELL_TYPE_ITEM,
value: { data: 'hello' }, value: { data: 'hello' },
i: 3, i: 3,
index: 3, index: 3,
@ -200,7 +200,7 @@ describe('calcCells', () => {
expect(called).toEqual(3); expect(called).toEqual(3);
expect(cells).toEqual([ expect(cells).toEqual([
{ {
type: CellType.Item, type: CELL_TYPE_ITEM,
value: 10, value: 10,
i: 0, i: 0,
index: 0, index: 0,
@ -209,7 +209,7 @@ describe('calcCells', () => {
visible: true, visible: true,
}, },
{ {
type: CellType.Item, type: CELL_TYPE_ITEM,
value: 9, value: 9,
i: 1, i: 1,
index: 1, index: 1,
@ -218,7 +218,7 @@ describe('calcCells', () => {
visible: true, visible: true,
}, },
{ {
type: CellType.Item, type: CELL_TYPE_ITEM,
value: 8, value: 8,
i: 2, i: 2,
index: 2, index: 2,
@ -258,7 +258,7 @@ describe('calcCells', () => {
expect(footerCalled).toEqual(3); expect(footerCalled).toEqual(3);
expect(cells).toEqual([ expect(cells).toEqual([
{ {
type: CellType.Header, type: CELL_TYPE_HEADER,
value: 'my header', value: 'my header',
i: 0, i: 0,
index: 0, index: 0,
@ -267,7 +267,7 @@ describe('calcCells', () => {
visible: false, visible: false,
}, },
{ {
type: CellType.Item, type: CELL_TYPE_ITEM,
value: '10', value: '10',
i: 1, i: 1,
index: 0, index: 0,
@ -276,7 +276,7 @@ describe('calcCells', () => {
visible: true, visible: true,
}, },
{ {
type: CellType.Item, type: CELL_TYPE_ITEM,
value: '9', value: '9',
i: 2, i: 2,
index: 1, index: 1,
@ -285,7 +285,7 @@ describe('calcCells', () => {
visible: true, visible: true,
}, },
{ {
type: CellType.Item, type: CELL_TYPE_ITEM,
value: '8', value: '8',
i: 3, i: 3,
index: 2, index: 2,
@ -294,7 +294,7 @@ describe('calcCells', () => {
visible: true, visible: true,
}, },
{ {
type: CellType.Footer, type: CELL_TYPE_FOOTER,
value: 'my footer', value: 'my footer',
i: 4, i: 4,
index: 2, 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 { export interface Cell {
i: number; i: number;
index: number; index: number;
@ -29,6 +17,8 @@ export interface VirtualNode {
visible: 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 HeaderFn = (item: any, index: number, items: any[]) => string | null | undefined;
export type ItemHeightFn = (item: any, index: number) => number; export type ItemHeightFn = (item: any, index: number) => number;
export type ItemRenderFn = (el: HTMLElement | null, cell: Cell, domIndex: number) => HTMLElement; 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 { 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 { export interface Viewport {
top: number; top: number;
@ -17,7 +18,7 @@ const MIN_READS = 2;
export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells: Cell[], range: Range) { export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells: Cell[], range: Range) {
// reset dom // reset dom
for (const node of dom) { for (const node of dom) {
node.change = NodeChange.NoChange; node.change = NODE_CHANGE_NONE;
node.d = true; node.d = true;
} }
@ -32,7 +33,7 @@ export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells:
const top = heightIndex[i]; const top = heightIndex[i];
if (top !== node.top) { if (top !== node.top) {
node.top = top; node.top = top;
node.change = NodeChange.Position; node.change = NODE_CHANGE_POSITION;
} }
node.d = false; node.d = false;
} else { } else {
@ -48,7 +49,7 @@ export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells:
const index = cell.index; const index = cell.index;
if (node) { if (node) {
node.d = false; node.d = false;
node.change = NodeChange.Cell; node.change = NODE_CHANGE_CELL;
node.cell = cell; node.cell = cell;
node.top = heightIndex[index]; node.top = heightIndex[index];
} else { } else {
@ -56,7 +57,7 @@ export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells:
d: false, d: false,
cell, cell,
visible: true, visible: true,
change: NodeChange.Cell, change: NODE_CHANGE_CELL,
top: heightIndex[index], top: heightIndex[index],
}); });
} }
@ -64,7 +65,7 @@ export function updateVDom(dom: VirtualNode[], heightIndex: Uint32Array, cells:
dom dom
.filter(n => n.d && n.top !== -9999) .filter(n => n.d && n.top !== -9999)
.forEach(n => { .forEach(n => {
n.change = NodeChange.Position; n.change = NODE_CHANGE_POSITION;
n.top = -9999; n.top = -9999;
}); });
} }
@ -83,7 +84,7 @@ export function doRender(
const cell = node.cell; const cell = node.cell;
// the cell change, the content must be updated // the cell change, the content must be updated
if (node.change === NodeChange.Cell) { if (node.change === NODE_CHANGE_CELL) {
if (i < childrenNu) { if (i < childrenNu) {
child = children[i] as HTMLElement; child = children[i] as HTMLElement;
nodeRender(child, cell, i); nodeRender(child, cell, i);
@ -99,7 +100,7 @@ export function doRender(
} }
// only update position when it changes // 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)`; 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 { function getTemplate(el: HTMLElement, type: CellType): HTMLTemplateElement | null {
switch (type) { switch (type) {
case CellType.Item: return el.querySelector('template:not([name])'); case CELL_TYPE_ITEM: return el.querySelector('template:not([name])');
case CellType.Header: return el.querySelector('template[name=header]'); case CELL_TYPE_HEADER: return el.querySelector('template[name=header]');
case CellType.Footer: return el.querySelector('template[name=footer]'); case CELL_TYPE_FOOTER: return el.querySelector('template[name=footer]');
} }
} }
@ -220,7 +221,7 @@ export function calcCells(
if (value != null) { if (value != null) {
cells.push({ cells.push({
i: j++, i: j++,
type: CellType.Header, type: CELL_TYPE_HEADER,
value, value,
index: i, index: i,
height: approxHeaderHeight, height: approxHeaderHeight,
@ -232,7 +233,7 @@ export function calcCells(
cells.push({ cells.push({
i: j++, i: j++,
type: CellType.Item, type: CELL_TYPE_ITEM,
value: item, value: item,
index: i, index: i,
height: itemHeight ? itemHeight(item, i) : approxItemHeight, height: itemHeight ? itemHeight(item, i) : approxItemHeight,
@ -245,7 +246,7 @@ export function calcCells(
if (value != null) { if (value != null) {
cells.push({ cells.push({
i: j++, i: j++,
type: CellType.Footer, type: CELL_TYPE_FOOTER,
value, value,
index: i, index: i,
height: approxFooterHeight, height: approxFooterHeight,
@ -283,7 +284,7 @@ export function resizeBuffer(buf: Uint32Array | undefined, len: number) {
} }
export function positionForIndex(index: number, cells: Cell[], heightIndex: Uint32Array): 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) { if (cell) {
return heightIndex[cell.i]; 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 { 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'; import { Range, calcCells, calcHeightIndex, doRender, findCellIndex, getRange, getShouldUpdate, getViewport, inplaceUpdate, positionForIndex, resizeBuffer, updateVDom } from './virtual-scroll-utils';
@Component({ @Component({
@ -411,9 +411,9 @@ export class VirtualScroll implements ComponentInterface {
private renderVirtualNode(node: VirtualNode) { private renderVirtualNode(node: VirtualNode) {
const { type, value, index } = node.cell; const { type, value, index } = node.cell;
switch (type) { switch (type) {
case CellType.Item: return this.renderItem!(value, index); case CELL_TYPE_ITEM: return this.renderItem!(value, index);
case CellType.Header: return this.renderHeader!(value, index); case CELL_TYPE_HEADER: return this.renderHeader!(value, index);
case CellType.Footer: return this.renderFooter!(value, index); case CELL_TYPE_FOOTER: return this.renderFooter!(value, index);
} }
} }

View File

@ -1,14 +1,2 @@
// lifecycle
export const enum ViewLifecycle {
WillEnter = 'ionViewWillEnter',
DidEnter = 'ionViewDidEnter',
WillLeave = 'ionViewWillLeave',
DidLeave = 'ionViewDidLeave',
WillUnload = 'ionViewWillUnload',
}
// util functions
export * from './utils/platform'; export * from './utils/platform';
export * from './utils/config'; export * from './utils/config';
export * from './utils/gesture/swipe-back';

View File

@ -4,18 +4,18 @@ export * from './index';
export * from './components/animation-controller/animation-interface'; export * from './components/animation-controller/animation-interface';
export * from './components/alert/alert-interface'; export * from './components/alert/alert-interface';
export * from './components/action-sheet/action-sheet-interface'; export * from './components/action-sheet/action-sheet-interface';
export * from './components/content/content-interface';
export * from './components/datetime/datetime-interface'; export * from './components/datetime/datetime-interface';
export * from './components/loading/loading-interface';
export * from './components/menu/menu-interface'; export * from './components/menu/menu-interface';
export * from './components/modal/modal-interface'; export * from './components/modal/modal-interface';
export * from './components/picker/picker-interface';
export * from './components/loading/loading-interface';
export * from './components/popover/popover-interface';
export * from './components/nav/nav-interface'; export * from './components/nav/nav-interface';
export * from './components/router/utils/interface'; export * from './components/picker/picker-interface';
export * from './components/popover/popover-interface';
export * from './components/range/range-interface'; export * from './components/range/range-interface';
export * from './components/router/utils/interface';
export * from './components/refresher/refresher-interface'; export * from './components/refresher/refresher-interface';
export * from './components/reorder-group/reorder-group-interface'; export * from './components/reorder-group/reorder-group-interface';
export * from './components/content/content-interface';
export * from './components/segment-button/segment-button-interface'; export * from './components/segment-button/segment-button-interface';
export * from './components/select/select-interface'; export * from './components/select/select-interface';
export * from './components/select-popover/select-popover-interface'; export * from './components/select-popover/select-popover-interface';
@ -26,7 +26,6 @@ export * from './components/virtual-scroll/virtual-scroll-interface';
// TODO: review how this types are exported // TODO: review how this types are exported
// Other types // Other types
export * from './components/nav/view-controller';
export { Gesture, GestureDetail } from './utils/gesture/gesture'; export { Gesture, GestureDetail } from './utils/gesture/gesture';

View File

@ -1,6 +1,6 @@
import { QueueApi } from '@stencil/core'; import { QueueApi } from '@stencil/core';
import { ViewLifecycle } from '..'; import { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from '../components/nav/constants';
import { Animation, AnimationBuilder, NavDirection, NavOptions } from '../interface'; import { Animation, AnimationBuilder, NavDirection, NavOptions } from '../interface';
const iosTransitionAnimation = () => import('./animations/ios.transition'); const iosTransitionAnimation = () => import('./animations/ios.transition');
@ -148,22 +148,22 @@ function playTransition(trans: Animation, opts: TransitionOptions): Promise<Anim
} }
function fireWillEvents(enteringEl: HTMLElement | undefined, leavingEl: HTMLElement | undefined) { function fireWillEvents(enteringEl: HTMLElement | undefined, leavingEl: HTMLElement | undefined) {
lifecycle(leavingEl, ViewLifecycle.WillLeave); lifecycle(leavingEl, LIFECYCLE_WILL_LEAVE);
lifecycle(enteringEl, ViewLifecycle.WillEnter); lifecycle(enteringEl, LIFECYCLE_WILL_ENTER);
} }
function fireDidEvents(enteringEl: HTMLElement | undefined, leavingEl: HTMLElement | undefined) { function fireDidEvents(enteringEl: HTMLElement | undefined, leavingEl: HTMLElement | undefined) {
lifecycle(enteringEl, ViewLifecycle.DidEnter); lifecycle(enteringEl, LIFECYCLE_DID_ENTER);
lifecycle(leavingEl, ViewLifecycle.DidLeave); lifecycle(leavingEl, LIFECYCLE_DID_LEAVE);
} }
export function lifecycle(el: HTMLElement | undefined, eventName: ViewLifecycle) { export function lifecycle(el: HTMLElement | undefined, eventName: string) {
if (el) { if (el) {
const event = new CustomEvent(eventName, { const ev = new CustomEvent(eventName, {
bubbles: false, bubbles: false,
cancelable: false, cancelable: false,
}); });
el.dispatchEvent(event); el.dispatchEvent(ev);
} }
} }

View File

@ -17,6 +17,7 @@
"no-unbound-method": true, "no-unbound-method": true,
"no-floating-promises": false, "no-floating-promises": false,
"no-invalid-template-strings": true, "no-invalid-template-strings": true,
"ban-export-const-enum": true,
"jsx-key": false, "jsx-key": false,
"jsx-self-close": false, "jsx-self-close": false,