mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c36b1fd2dc |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -18,8 +18,8 @@ dist/
|
||||
node_modules/
|
||||
tmp/
|
||||
temp/
|
||||
core/theme-builder/
|
||||
core/test-components/
|
||||
packages/core/theme-builder/
|
||||
packages/core/test-components/
|
||||
$RECYCLE.BIN/
|
||||
|
||||
.DS_Store
|
||||
|
||||
@@ -8,8 +8,8 @@ Ionic is based on [Web Components](https://www.webcomponents.org/introduction) a
|
||||
|
||||
# Packages
|
||||
|
||||
- [Core](core/README.md)
|
||||
- [Ionic Angular](angular/README.md)
|
||||
- [Core](packages/core/README.md)
|
||||
- [Ionic Angular](packages/ionic-angular/README.md)
|
||||
|
||||
|
||||
### Getting Started
|
||||
@@ -46,8 +46,3 @@ As Ionic components migrate to the web component standard, a goal of ours is to
|
||||
|
||||
The source code for Ionic V1 has been moved to [ionic-team/ionic-v1](https://github.com/ionic-team/ionic-v1).
|
||||
Please open any issues and pull requests related to Ionic V1 on that repository.
|
||||
|
||||
|
||||
### Ionic V3
|
||||
|
||||
The source code for Ionic V3 has been moved to the [v3 branch](https://github.com/ionic-team/ionic/tree/v3).
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# npm link local development
|
||||
|
||||
`npm link` doesn't work as expected due to the `devDependency` on `@angular/core`. This is the work around...
|
||||
|
||||
npm run build.link ../ionic-conference-app
|
||||
|
||||
When the command above is ran from the `angular` directory, it will build `@ionic/angular` and copy the `dist` directory to the correct location of another local project. In the example above, the end result is that it copies the `dist` directory to `../ionic-conference-app/node_modules/@ionic/angular/dist`. The path given should be relative to the root of this mono repo.
|
||||
|
||||
|
||||
# Deploy
|
||||
|
||||
1. `npm run prepare.deploy`
|
||||
2. Review/update changelog
|
||||
3. Commit updates using the package name and version number as the commit message.
|
||||
4. `npm run deploy`
|
||||
5. :tada:
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActionSheetOptions } from '@ionic/core';
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@Injectable()
|
||||
export class ActionSheetController extends OverlayBaseController<ActionSheetOptions, HTMLIonActionSheetElement> {
|
||||
constructor() {
|
||||
super('ion-action-sheet-controller');
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AlertOptions } from '@ionic/core';
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@Injectable()
|
||||
export class AlertController extends OverlayBaseController<AlertOptions, HTMLIonAlertElement> {
|
||||
constructor() {
|
||||
super('ion-alert-controller');
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
import {
|
||||
ApplicationRef,
|
||||
ComponentFactoryResolver,
|
||||
Injectable,
|
||||
Injector,
|
||||
} from '@angular/core';
|
||||
|
||||
import { FrameworkDelegate } from '@ionic/core';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class AngularDelegate {
|
||||
|
||||
constructor(
|
||||
private appRef: ApplicationRef
|
||||
) {}
|
||||
|
||||
create(cfr: ComponentFactoryResolver, injector: Injector) {
|
||||
return new AngularFrameworkDelegate(cfr, injector, this.appRef);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class AngularFrameworkDelegate implements FrameworkDelegate {
|
||||
|
||||
private elRefMap = new WeakMap<HTMLElement, any>();
|
||||
|
||||
constructor(
|
||||
private cfr: ComponentFactoryResolver,
|
||||
private injector: Injector,
|
||||
private appRef: ApplicationRef
|
||||
) {}
|
||||
|
||||
attachViewToDom(container: any, component: any, data?: any, cssClasses?: string[]): Promise<any> {
|
||||
|
||||
const componentFactory = this.cfr.resolveComponentFactory(component);
|
||||
const hostElement = document.createElement(componentFactory.selector);
|
||||
if (data) {
|
||||
Object.assign(hostElement, data);
|
||||
}
|
||||
|
||||
const childInjector = Injector.create([], this.injector);
|
||||
const componentRef = componentFactory.create(childInjector, [], hostElement);
|
||||
for (const clazz of cssClasses) {
|
||||
hostElement.classList.add(clazz);
|
||||
}
|
||||
|
||||
container.appendChild(hostElement);
|
||||
|
||||
this.appRef.attachView(componentRef.hostView);
|
||||
this.elRefMap.set(hostElement, componentRef);
|
||||
return Promise.resolve(hostElement);
|
||||
}
|
||||
|
||||
removeViewFromDom(_container: any, component: any): Promise<void> {
|
||||
const mountingData = this.elRefMap.get(component);
|
||||
if (mountingData) {
|
||||
mountingData.componentRef.destroy();
|
||||
this.elRefMap.delete(component);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { LoadingOptions } from '@ionic/core';
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@Injectable()
|
||||
export class LoadingController extends OverlayBaseController<LoadingOptions, HTMLIonLoadingElement> {
|
||||
constructor() {
|
||||
super('ion-loading-controller');
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { proxyMethod } from '../util/util';
|
||||
|
||||
const CTRL = 'ion-menu-controller';
|
||||
@Injectable()
|
||||
export class MenuController {
|
||||
|
||||
/**
|
||||
* Programatically open the Menu.
|
||||
* @param {string} [menuId] Optionally get the menu by its id, or side.
|
||||
* @return {Promise} returns a promise when the menu is fully opened
|
||||
*/
|
||||
open(menuId?: string): Promise<boolean> {
|
||||
return proxyMethod(CTRL, 'open', menuId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Programatically close the Menu. If no `menuId` is given as the first
|
||||
* argument then it'll close any menu which is open. If a `menuId`
|
||||
* is given then it'll close that exact menu.
|
||||
* @param {string} [menuId] Optionally get the menu by its id, or side.
|
||||
* @return {Promise} returns a promise when the menu is fully closed
|
||||
*/
|
||||
close(menuId?: string): Promise<boolean> {
|
||||
return proxyMethod(CTRL, 'close', menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the menu. If it's closed, it will open, and if opened, it
|
||||
* will close.
|
||||
* @param {string} [menuId] Optionally get the menu by its id, or side.
|
||||
* @return {Promise} returns a promise when the menu has been toggled
|
||||
*/
|
||||
toggle(menuId?: string): Promise<boolean> {
|
||||
return proxyMethod(CTRL, 'toggle', menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to enable or disable a menu. For example, there could be multiple
|
||||
* left menus, but only one of them should be able to be opened at the same
|
||||
* time. If there are multiple menus on the same side, then enabling one menu
|
||||
* will also automatically disable all the others that are on the same side.
|
||||
* @param {string} [menuId] Optionally get the menu by its id, or side.
|
||||
* @return {HTMLIonMenuElement} Returns the instance of the menu, which is useful for chaining.
|
||||
*/
|
||||
enable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement> {
|
||||
return proxyMethod(CTRL, 'enable', shouldEnable, menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to enable or disable the ability to swipe open the menu.
|
||||
* @param {boolean} shouldEnable True if it should be swipe-able, false if not.
|
||||
* @param {string} [menuId] Optionally get the menu by its id, or side.
|
||||
* @return {HTMLIonMenuElement} Returns the instance of the menu, which is useful for chaining.
|
||||
*/
|
||||
swipeEnable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement> {
|
||||
return proxyMethod(CTRL, 'swipeEnable', shouldEnable, menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [menuId] Optionally get the menu by its id, or side.
|
||||
* @return {boolean} Returns true if the specified menu is currently open, otherwise false.
|
||||
* If the menuId is not specified, it returns true if ANY menu is currenly open.
|
||||
*/
|
||||
isOpen(menuId?: string): Promise<boolean> {
|
||||
return proxyMethod(CTRL, 'isOpen', menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [menuId] Optionally get the menu by its id, or side.
|
||||
* @return {boolean} Returns true if the menu is currently enabled, otherwise false.
|
||||
*/
|
||||
isEnabled(menuId?: string): Promise<boolean> {
|
||||
return proxyMethod(CTRL, 'isEnabled', menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to get a menu instance. If a `menuId` is not provided then it'll
|
||||
* return the first menu found. If a `menuId` is `left` or `right`, then
|
||||
* it'll return the enabled menu on that side. Otherwise, if a `menuId` is
|
||||
* provided, then it'll try to find the menu using the menu's `id`
|
||||
* property. If a menu is not found then it'll return `null`.
|
||||
* @param {string} [menuId] Optionally get the menu by its id, or side.
|
||||
* @return {HTMLIonMenuElement} Returns the instance of the menu if found, otherwise `null`.
|
||||
*/
|
||||
get(menuId?: string): Promise<HTMLIonMenuElement> {
|
||||
return proxyMethod(CTRL, 'get', menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Menu} Returns the instance of the menu already opened, otherwise `null`.
|
||||
*/
|
||||
getOpen(): Promise<HTMLIonMenuElement> {
|
||||
return proxyMethod(CTRL, 'getOpen');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Array<HTMLIonMenuElement>} Returns an array of all menu instances.
|
||||
*/
|
||||
getMenus(): Promise<HTMLIonMenuElement[]> {
|
||||
return proxyMethod(CTRL, 'getMenus');
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
|
||||
import { ModalOptions } from '@ionic/core';
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
import { AngularDelegate } from './angular-delegate';
|
||||
|
||||
@Injectable()
|
||||
export class ModalController extends OverlayBaseController<ModalOptions, HTMLIonModalElement> {
|
||||
constructor(
|
||||
private cfr: ComponentFactoryResolver,
|
||||
private injector: Injector,
|
||||
private angularDelegate: AngularDelegate,
|
||||
) {
|
||||
super('ion-modal-controller');
|
||||
}
|
||||
|
||||
create(opts?: ModalOptions): Promise<HTMLIonModalElement> {
|
||||
return super.create({
|
||||
...opts,
|
||||
delegate: this.angularDelegate.create(this.cfr, this.injector)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { PickerOptions } from '@ionic/core';
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@Injectable()
|
||||
export class PickerController extends OverlayBaseController<PickerOptions, HTMLIonPickerElement> {
|
||||
constructor() {
|
||||
super('ion-picker-controller');
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
|
||||
import { PopoverOptions } from '@ionic/core';
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
import { AngularDelegate } from './angular-delegate';
|
||||
|
||||
@Injectable()
|
||||
export class PopoverController extends OverlayBaseController<PopoverOptions, HTMLIonPopoverElement> {
|
||||
constructor(
|
||||
private cfr: ComponentFactoryResolver,
|
||||
private injector: Injector,
|
||||
private angularDelegate: AngularDelegate,
|
||||
) {
|
||||
super('ion-popover-controller');
|
||||
}
|
||||
|
||||
create(opts?: PopoverOptions): Promise<HTMLIonPopoverElement> {
|
||||
return super.create({
|
||||
...opts,
|
||||
delegate: this.angularDelegate.create(this.cfr, this.injector)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ToastOptions } from '@ionic/core';
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@Injectable()
|
||||
export class ToastController extends OverlayBaseController<ToastOptions, HTMLIonToastElement> {
|
||||
constructor() {
|
||||
super('ion-toast-controller');
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { proxyMethod } from '../util/util';
|
||||
|
||||
export class OverlayBaseController<Opts, Overlay> {
|
||||
constructor(private ctrl: string) {}
|
||||
|
||||
create(opts?: Opts): Promise<Overlay> {
|
||||
return proxyMethod(this.ctrl, 'create', opts);
|
||||
}
|
||||
|
||||
dismiss(data?: any, role?: string, id = -1): Promise<void> {
|
||||
return proxyMethod(this.ctrl, 'dismiss', data, role, id);
|
||||
}
|
||||
|
||||
getTop(): Promise<Overlay> {
|
||||
return proxyMethod(this.ctrl, 'getTop');
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
export function proxyMethod(ctrlName: string, methodName: string, ...args: any[]) {
|
||||
const controller = ensureElementInBody(ctrlName);
|
||||
return controller.componentOnReady()
|
||||
.then(() => (controller as any)[methodName].apply(controller, args));
|
||||
}
|
||||
|
||||
export function ensureElementInBody(elementName: string) {
|
||||
let element = document.querySelector(elementName);
|
||||
if (!element) {
|
||||
element = document.createElement(elementName);
|
||||
document.body.appendChild(element);
|
||||
}
|
||||
return element as HTMLStencilElement;
|
||||
}
|
||||
43
circle.yml
43
circle.yml
@@ -3,30 +3,45 @@ jobs:
|
||||
build:
|
||||
working_directory: ~/ionic/
|
||||
docker:
|
||||
- image: node:8
|
||||
- image: node:7
|
||||
branches:
|
||||
# ignore:
|
||||
# - core
|
||||
ignore:
|
||||
- core
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: ionic-site
|
||||
- run:
|
||||
name: Prepare ionic-site repo
|
||||
command: ./packages/ionic-angular/scripts/docs/prepare.sh
|
||||
- save_cache:
|
||||
key: ionic-site
|
||||
paths:
|
||||
- ~/ionic-site/
|
||||
- restore_cache:
|
||||
key: node_modules_{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: Install monorepo node modules
|
||||
name: Install node modules
|
||||
command: npm i
|
||||
- save_cache:
|
||||
key: node_modules_{{ checksum "package.json" }}
|
||||
paths:
|
||||
- ~/ionic/node_modules/
|
||||
- restore_cache:
|
||||
key: node_modules_{{ checksum "core/package.json" }}
|
||||
- run:
|
||||
name: Install core node modules
|
||||
command: npm --prefix core/ i
|
||||
- save_cache:
|
||||
key: node_modules_{{ checksum "core/package.json" }}
|
||||
paths:
|
||||
- ~/ionic/core/node_modules/
|
||||
name: Run tslint
|
||||
command: ./node_modules/.bin/gulp lint.ts
|
||||
- run:
|
||||
name: Validate Core
|
||||
command: npm --prefix core/ run validate
|
||||
name: Build Demos
|
||||
command: |
|
||||
if [ "${CIRCLE_BRANCH}" == "master" ]; then
|
||||
./node_modules/.bin/gulp demos.prod --production=true --batch=$CIRCLE_NODE_INDEX --batches=$CIRCLE_NODE_TOTAL
|
||||
fi
|
||||
- add_ssh_keys
|
||||
- deploy:
|
||||
name: Update docs
|
||||
command: |
|
||||
if [ "${CIRCLE_BRANCH}" == "master" ]; then
|
||||
./packages/ionic-angular/scripts/ci/deploy.sh
|
||||
else
|
||||
echo "We are on ${CIRCLE_BRANCH} branch, not going to update docs."
|
||||
fi
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
<a name="0.1.4-6"></a>
|
||||
## [0.1.4-6](https://github.com/ionic-team/ionic/compare/v0.1.4-5...v0.1.4-6) (2018-03-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** backdrop calls cancel handler ([de22eca](https://github.com/ionic-team/ionic/commit/de22eca))
|
||||
* **alert:** set input value ([6e2a9c9](https://github.com/ionic-team/ionic/commit/6e2a9c9))
|
||||
* **angular:** create angular delegate ([3b5f758](https://github.com/ionic-team/ionic/commit/3b5f758))
|
||||
* **angular:** fix overlays ([cc4fecc](https://github.com/ionic-team/ionic/commit/cc4fecc))
|
||||
* **angular:** modal and popover support ([9a0755a](https://github.com/ionic-team/ionic/commit/9a0755a))
|
||||
* **demos:** fixes angular ([f398b3a](https://github.com/ionic-team/ionic/commit/f398b3a))
|
||||
* **overlay:** using hostData for zIndex ([64f0866](https://github.com/ionic-team/ionic/commit/64f0866))
|
||||
* **overlay:** wrong stencil import ([22f6a34](https://github.com/ionic-team/ionic/commit/22f6a34))
|
||||
* **overlays:** OverlayController interface ([6e2ca85](https://github.com/ionic-team/ionic/commit/6e2ca85))
|
||||
* **popover:** lifecycles ([b56c2a8](https://github.com/ionic-team/ionic/commit/b56c2a8))
|
||||
* **router:** ambiguous routes ([b4f46ee](https://github.com/ionic-team/ionic/commit/b4f46ee))
|
||||
* **router:** fix selection ([207f416](https://github.com/ionic-team/ionic/commit/207f416))
|
||||
* **router:** rename API to match stencil-router ([e729610](https://github.com/ionic-team/ionic/commit/e729610))
|
||||
* **router:** retuning string path ([f48d817](https://github.com/ionic-team/ionic/commit/f48d817))
|
||||
* **toggle:** ios shadow ([7df023a](https://github.com/ionic-team/ionic/commit/7df023a))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ion-router:** dynamic routes ([7c3cba0](https://github.com/ionic-team/ionic/commit/7c3cba0))
|
||||
* **overlay:** adds lifecycle events ([0b099ce](https://github.com/ionic-team/ionic/commit/0b099ce))
|
||||
* **overlays:** adds onDidDismiss and onWillDismiss ([7dcf8a5](https://github.com/ionic-team/ionic/commit/7dcf8a5))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **scss:** optimize multi dir ([#14156](https://github.com/ionic-team/ionic/issues/14156)) ([ba63d01](https://github.com/ionic-team/ionic/commit/ba63d01))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.4-5"></a>
|
||||
## [0.1.4-5](https://github.com/ionic-team/ionic/compare/v0.1.4-4...v0.1.4-5) (2018-03-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **item:** button outline ([f671008](https://github.com/ionic-team/ionic/commit/f671008))
|
||||
* **router:** fix flickering ([f2ac6e3](https://github.com/ionic-team/ionic/commit/f2ac6e3))
|
||||
* **router:** flickering 2 ([88f2981](https://github.com/ionic-team/ionic/commit/88f2981))
|
||||
* **router:** tslint ([1ace045](https://github.com/ionic-team/ionic/commit/1ace045))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **router:** adds redirectTo ([f5c6333](https://github.com/ionic-team/ionic/commit/f5c6333))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.4-4"></a>
|
||||
## [0.1.4-4](https://github.com/ionic-team/ionic/compare/v0.1.4-3...v0.1.4-4) (2018-03-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **back-button:** implements back animation ([64a787a](https://github.com/ionic-team/ionic/commit/64a787a))
|
||||
* **route:** params is not undefined ([8b6df5a](https://github.com/ionic-team/ionic/commit/8b6df5a))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.4-3"></a>
|
||||
## [0.1.4-3](https://github.com/ionic-team/ionic/compare/v0.1.4-2...v0.1.4-3) (2018-03-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **router:** passing params to ion-nav ([d1263a8](https://github.com/ionic-team/ionic/commit/d1263a8))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **back-button:** adds defaultHref ([5271f68](https://github.com/ionic-team/ionic/commit/5271f68))
|
||||
* **nav:** params ([878d7e6](https://github.com/ionic-team/ionic/commit/878d7e6))
|
||||
* **route:** adds route-link ([4a3030f](https://github.com/ionic-team/ionic/commit/4a3030f))
|
||||
* **router:** reverse lookup with params ([3ce8a67](https://github.com/ionic-team/ionic/commit/3ce8a67))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.4-2"></a>
|
||||
## [0.1.4-2](https://github.com/ionic-team/ionic/compare/v0.1.4-0...v0.1.4-2) (2018-03-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **fab:** add side as a property for fab list ([7387d34](https://github.com/ionic-team/ionic/commit/7387d34))
|
||||
* **ion-router:** fixes routing algorithm ([c8a27b7](https://github.com/ionic-team/ionic/commit/c8a27b7))
|
||||
* **item:** href ([540c33b](https://github.com/ionic-team/ionic/commit/540c33b))
|
||||
* **overlays:** bundling of overlays ([9650bec](https://github.com/ionic-team/ionic/commit/9650bec))
|
||||
* **router:** invalid url ([c7fe694](https://github.com/ionic-team/ionic/commit/c7fe694))
|
||||
* **routing:** flickering (part 1) ([7b264f9](https://github.com/ionic-team/ionic/commit/7b264f9))
|
||||
* **tabs:** do not select when using router ([174d9b5](https://github.com/ionic-team/ionic/commit/174d9b5))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **router:** adds parameters ([f29e3f4](https://github.com/ionic-team/ionic/commit/f29e3f4))
|
||||
* **virtual-scroll:** adds JSX support ([dc8b363](https://github.com/ionic-team/ionic/commit/dc8b363))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.4-1"></a>
|
||||
## [0.1.4-1](https://github.com/ionic-team/ionic/compare/v0.1.4-0...v0.1.4-1) (2018-03-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ion-router:** fixes routing algorithm ([c8a27b7](https://github.com/ionic-team/ionic/commit/c8a27b7))
|
||||
* **overlays:** bundling of overlays ([9650bec](https://github.com/ionic-team/ionic/commit/9650bec))
|
||||
* **routing:** flickering (part 1) ([7b264f9](https://github.com/ionic-team/ionic/commit/7b264f9))
|
||||
* **tabs:** do not select when using router ([174d9b5](https://github.com/ionic-team/ionic/commit/174d9b5))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **virtual-scroll:** adds JSX support ([dc8b363](https://github.com/ionic-team/ionic/commit/dc8b363))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.4-0"></a>
|
||||
## [0.1.4-0](https://github.com/ionic-team/ionic/compare/v0.1.3...v0.1.4-0) (2018-03-06)
|
||||
|
||||
### Refactor
|
||||
|
||||
- Refactored navigation system
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **testing:** do not throw error for missing Ionic global ([aa91d11](https://github.com/ionic-team/ionic/commit/aa91d11))
|
||||
* **zone:** forgot to remove console.logs ([4ec3e48](https://github.com/ionic-team/ionic/commit/4ec3e48))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.3"></a>
|
||||
## [0.1.3](https://github.com/ionic-team/ionic/compare/v0.1.2...v0.1.3) (2018-03-03)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* Updates to latest stencil, that includes the zone bypassing abilities.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ts:** ts strict fixes ([8ff02c7](https://github.com/ionic-team/ionic/commit/8ff02c7))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.2"></a>
|
||||
## [0.1.2](https://github.com/ionic-team/ionic/compare/v0.1.1...v0.1.2) (2018-03-03)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **events:** bypass ngzone ([7366c38](https://github.com/ionic-team/ionic/commit/7366c38))
|
||||
* **scroll:** watchdog + simplification ([33a6274](https://github.com/ionic-team/ionic/commit/33a6274))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **scroll:** clearInterval just to be safe ([6da9882](https://github.com/ionic-team/ionic/commit/6da9882))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.1"></a>
|
||||
## [0.1.1](https://github.com/ionic-team/ionic/commit/291e85e61128b2f3101d9cea6b42d4cf751dc481) (2018-03-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **button:** pass the property type instead of hardcoding button ([10e481a](https://github.com/ionic-team/ionic/commit/10e481a))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.0"></a>
|
||||
## [0.1.0](https://github.com/ionic-team/ionic/commit/43a8c4c7a719169336a84964fc1c737562d764a6) (2018-03-01)
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Component, Prop } from '@stencil/core';
|
||||
import { openURL } from '../../utils/theme';
|
||||
|
||||
|
||||
@Component({
|
||||
tag: 'ion-anchor'
|
||||
})
|
||||
export class Anchor {
|
||||
|
||||
@Prop() href: string;
|
||||
|
||||
render() {
|
||||
return <a
|
||||
href={this.href}
|
||||
onClick={(ev) => openURL(this.href, ev)}><slot/></a>;
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import { Component, Element, Prop } from '@stencil/core';
|
||||
import { Config } from '../../index';
|
||||
import { openURL } from '../../utils/theme';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-back-button',
|
||||
styleUrls: {
|
||||
ios: 'back-button.ios.scss',
|
||||
md: 'back-button.md.scss'
|
||||
},
|
||||
host: {
|
||||
theme: 'back-button'
|
||||
}
|
||||
})
|
||||
export class BackButton {
|
||||
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
* Possible values are: `"ios"` or `"md"`.
|
||||
* For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
|
||||
*/
|
||||
@Prop() mode: 'ios' | 'md';
|
||||
|
||||
/**
|
||||
* The text property is used to provide custom text for the back button while using the
|
||||
* default look-and-feel
|
||||
*/
|
||||
@Prop() text: string|undefined;
|
||||
|
||||
@Prop() icon: string;
|
||||
|
||||
@Prop() defaultHref: string;
|
||||
|
||||
@Prop({ context: 'config' }) config: Config;
|
||||
|
||||
@Element() el: HTMLElement;
|
||||
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
'show-back-button': !!this.defaultHref
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private onClick(ev: Event) {
|
||||
const nav = this.el.closest('ion-nav');
|
||||
if (nav && nav.canGoBack()) {
|
||||
ev.preventDefault();
|
||||
nav.pop();
|
||||
} else if (this.defaultHref) {
|
||||
openURL(this.defaultHref, ev, true);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const backButtonIcon = this.icon || this.config.get('backButtonIcon', 'arrow-back');
|
||||
const backButtonText = this.text || this.config.get('backButtonText', this.mode === 'ios' ? 'Back' : '');
|
||||
|
||||
return (
|
||||
<button
|
||||
class='back-button-inner'
|
||||
onClick={(ev) => this.onClick(ev)}>
|
||||
{ backButtonIcon && <ion-icon name={backButtonIcon}/> }
|
||||
{ backButtonText && <span class='button-text'>{backButtonText}</span> }
|
||||
{ this.mode === 'md' && <ion-ripple-effect/> }
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { By, until } = require('selenium-webdriver');
|
||||
const { register, Page, platforms } = require('../../../../../scripts/e2e');
|
||||
|
||||
class E2ETestPage extends Page {
|
||||
constructor(driver, platform) {
|
||||
super(driver, `http://localhost:3333/src/components/list/test/inset?ionicplatform=${platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
platforms.forEach(platform => {
|
||||
describe('list/inset', () => {
|
||||
register('should init', driver => {
|
||||
const page = new E2ETestPage(driver, platform);
|
||||
return page.navigate('#content');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,70 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>List - Basic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<script src="/dist/ionic.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<ion-app>
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>List - Inset</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content">
|
||||
<ion-list inset>
|
||||
<ion-item>
|
||||
<ion-label>Normal Input</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Stacked Input</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Normal Select</ion-label>
|
||||
<ion-select>
|
||||
<ion-select-option>1</ion-select-option>
|
||||
<ion-select-option>2</ion-select-option>
|
||||
<ion-select-option>3</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Stacked Select</ion-label>
|
||||
<ion-select>
|
||||
<ion-select-option>1</ion-select-option>
|
||||
<ion-select-option>2</ion-select-option>
|
||||
<ion-select-option>3</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Normal Datetime</ion-label>
|
||||
<ion-datetime></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Stacked Datetime</ion-label>
|
||||
<ion-datetime></ion-datetime>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
|
||||
</ion-app>
|
||||
|
||||
<style>
|
||||
ion-content .scroll {
|
||||
background: #e5e5e5;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
import { Component, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core';
|
||||
import { Animation, AnimationBuilder, Config, FrameworkDelegate } from '../../index';
|
||||
|
||||
import { createThemedClasses, getClassMap } from '../../utils/theme';
|
||||
import { BACKDROP, OverlayEventDetail, OverlayInterface, attachComponent, dismiss, eventMethod, present } from '../../utils/overlays';
|
||||
|
||||
import iosEnterAnimation from './animations/ios.enter';
|
||||
import iosLeaveAnimation from './animations/ios.leave';
|
||||
|
||||
import mdEnterAnimation from './animations/md.enter';
|
||||
import mdLeaveAnimation from './animations/md.leave';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-modal',
|
||||
styleUrls: {
|
||||
ios: 'modal.ios.scss',
|
||||
md: 'modal.md.scss'
|
||||
},
|
||||
host: {
|
||||
theme: 'modal'
|
||||
}
|
||||
})
|
||||
export class Modal implements OverlayInterface {
|
||||
|
||||
private usersElement: HTMLElement;
|
||||
|
||||
animation: Animation|undefined;
|
||||
presented = false;
|
||||
|
||||
@Element() el: HTMLElement;
|
||||
|
||||
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: HTMLIonAnimationControllerElement;
|
||||
@Prop({ context: 'config' }) config: Config;
|
||||
|
||||
@Prop() overlayId: number;
|
||||
@Prop() delegate: FrameworkDelegate;
|
||||
|
||||
/**
|
||||
* The color to use from your Sass `$colors` map.
|
||||
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
|
||||
* For more information, see [Theming your App](/docs/theming/theming-your-app).
|
||||
*/
|
||||
@Prop() color: string;
|
||||
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
* Possible values are: `"ios"` or `"md"`.
|
||||
* For more information, see [Platform Styles](/docs/theming/platform-specific-styles).
|
||||
*/
|
||||
@Prop() mode: 'ios' | 'md';
|
||||
|
||||
/**
|
||||
* Animation to use when the modal is presented.
|
||||
*/
|
||||
@Prop() enterAnimation: AnimationBuilder;
|
||||
|
||||
/**
|
||||
* Animation to use when the modal is dismissed.
|
||||
*/
|
||||
@Prop() leaveAnimation: AnimationBuilder;
|
||||
|
||||
/**
|
||||
* The component to display inside of the modal.
|
||||
*/
|
||||
@Prop() component: any;
|
||||
|
||||
/**
|
||||
* The data to pass to the modal component.
|
||||
*/
|
||||
@Prop() data: any = {};
|
||||
|
||||
/**
|
||||
* Additional classes to apply for custom CSS. If multiple classes are
|
||||
* provided they should be separated by spaces.
|
||||
*/
|
||||
@Prop() cssClass: string;
|
||||
|
||||
/**
|
||||
* If true, the modal will be dismissed when the backdrop is clicked. Defaults to `true`.
|
||||
*/
|
||||
@Prop() enableBackdropDismiss = true;
|
||||
|
||||
/**
|
||||
* If true, a backdrop will be displayed behind the modal. Defaults to `true`.
|
||||
*/
|
||||
@Prop() showBackdrop = true;
|
||||
|
||||
/**
|
||||
* If true, the modal will animate. Defaults to `true`.
|
||||
*/
|
||||
@Prop() willAnimate = true;
|
||||
|
||||
/**
|
||||
* Emitted after the modal has loaded.
|
||||
*/
|
||||
@Event() ionModalDidLoad: EventEmitter<void>;
|
||||
|
||||
/**
|
||||
* Emitted after the modal has unloaded.
|
||||
*/
|
||||
@Event() ionModalDidUnload: EventEmitter<void>;
|
||||
|
||||
/**
|
||||
* Emitted after the modal has presented.
|
||||
*/
|
||||
@Event({eventName: 'ionModalDidPresent'}) didPresent: EventEmitter<void>;
|
||||
|
||||
/**
|
||||
* Emitted before the modal has presented.
|
||||
*/
|
||||
@Event({eventName: 'ionModalWillPresent'}) willPresent: EventEmitter<void>;
|
||||
|
||||
/**
|
||||
* Emitted before the modal has dismissed.
|
||||
*/
|
||||
@Event({eventName: 'ionModalWillDismiss'}) willDismiss: EventEmitter<OverlayEventDetail>;
|
||||
|
||||
/**
|
||||
* Emitted after the modal has dismissed.
|
||||
*/
|
||||
@Event({eventName: 'ionModalDidDismiss'}) didDismiss: EventEmitter<OverlayEventDetail>;
|
||||
|
||||
componentDidLoad() {
|
||||
this.ionModalDidLoad.emit();
|
||||
}
|
||||
|
||||
componentDidUnload() {
|
||||
this.ionModalDidUnload.emit();
|
||||
}
|
||||
|
||||
@Listen('ionDismiss')
|
||||
protected onDismiss(ev: UIEvent) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
|
||||
this.dismiss();
|
||||
}
|
||||
|
||||
@Listen('ionBackdropTap')
|
||||
protected onBackdropTap() {
|
||||
this.dismiss(null, BACKDROP);
|
||||
}
|
||||
|
||||
@Listen('ionModalDidPresent')
|
||||
@Listen('ionModalWillPresent')
|
||||
@Listen('ionModalWillDismiss')
|
||||
@Listen('ionModalDidDismiss')
|
||||
protected lifecycle(modalEvent: CustomEvent) {
|
||||
const el = this.usersElement;
|
||||
const name = LIFECYCLE_MAP[modalEvent.type];
|
||||
if (el && name) {
|
||||
const event = new CustomEvent(name, {
|
||||
bubbles: false,
|
||||
cancelable: false,
|
||||
detail: modalEvent.detail
|
||||
});
|
||||
el.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Present the modal overlay after it has been created.
|
||||
*/
|
||||
@Method()
|
||||
present(): Promise<void> {
|
||||
if (this.presented) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
const container = this.el.querySelector(`.modal-wrapper`);
|
||||
const data = {
|
||||
...this.data,
|
||||
modal: this.el
|
||||
};
|
||||
const classes = {
|
||||
...getClassMap(this.cssClass),
|
||||
'ion-page': true
|
||||
};
|
||||
return attachComponent(this.delegate, container, this.component, classes, data)
|
||||
.then(el => this.usersElement = el)
|
||||
.then(() => present(this, 'modalEnter', iosEnterAnimation, mdEnterAnimation, undefined));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss the modal overlay after it has been presented.
|
||||
*/
|
||||
@Method()
|
||||
dismiss(data?: any, role?: string): Promise<void> {
|
||||
return dismiss(this, data, role, 'modalLeave', iosLeaveAnimation, mdLeaveAnimation, undefined);
|
||||
}
|
||||
|
||||
@Method()
|
||||
onDidDismiss(callback: (data?: any, role?: string) => void): Promise<OverlayEventDetail> {
|
||||
return eventMethod(this.el, 'ionModalDidDismiss', callback);
|
||||
}
|
||||
|
||||
@Method()
|
||||
onWillDismiss(callback: (data?: any, role?: string) => void): Promise<OverlayEventDetail> {
|
||||
return eventMethod(this.el, 'ionModalWillDismiss', callback);
|
||||
}
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
'no-router': true,
|
||||
style: {
|
||||
zIndex: 20000 + this.overlayId,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const dialogClasses = createThemedClasses(this.mode, this.color, 'modal-wrapper');
|
||||
|
||||
return [
|
||||
<ion-backdrop visible={this.showBackdrop} tappable={this.enableBackdropDismiss}/>,
|
||||
<div role='dialog' class={dialogClasses}></div>
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
const LIFECYCLE_MAP: any = {
|
||||
'ionModalDidPresent': 'ionViewDidEnter',
|
||||
'ionModalWillPresent': 'ionViewWillEnter',
|
||||
'ionModalWillDismiss': 'ionViewWillDismiss',
|
||||
'ionModalDidDismiss': 'ionViewDidDismiss',
|
||||
};
|
||||
|
||||
export interface ModalOptions {
|
||||
component: any;
|
||||
data?: any;
|
||||
showBackdrop?: boolean;
|
||||
enableBackdropDismiss?: boolean;
|
||||
enterAnimation?: AnimationBuilder;
|
||||
leaveAnimation?: AnimationBuilder;
|
||||
cssClass?: string;
|
||||
delegate?: FrameworkDelegate;
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
import { ViewController, isViewController } from './view-controller';
|
||||
import { NavControllerBase } from './nav';
|
||||
import { Transition } from './transition';
|
||||
|
||||
export function convertToView(page: any, params: any): ViewController {
|
||||
if (!page) {
|
||||
return null;
|
||||
}
|
||||
if (isViewController(page)) {
|
||||
return page;
|
||||
}
|
||||
return new ViewController(page, params);
|
||||
}
|
||||
|
||||
export function convertToViews(pages: any[]): ViewController[] {
|
||||
return pages.map(page => {
|
||||
if (isViewController(page)) {
|
||||
return page;
|
||||
}
|
||||
if ('page' in page) {
|
||||
return convertToView(page.page, page.params);
|
||||
}
|
||||
return convertToView(page, undefined);
|
||||
})
|
||||
.filter(v => v !== null);
|
||||
}
|
||||
|
||||
export function setZIndex(nav: NavControllerBase, enteringView: ViewController, leavingView: ViewController, direction: string) {
|
||||
if (enteringView) {
|
||||
|
||||
leavingView = leavingView || nav.getPrevious(enteringView);
|
||||
|
||||
if (leavingView && isPresent(leavingView._zIndex)) {
|
||||
if (direction === NavDirection.back) {
|
||||
enteringView._setZIndex(leavingView._zIndex - 1);
|
||||
|
||||
} else {
|
||||
enteringView._setZIndex(leavingView._zIndex + 1);
|
||||
}
|
||||
|
||||
} else {
|
||||
enteringView._setZIndex(INIT_ZINDEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isPresent(val: any): val is any {
|
||||
return val !== undefined && val !== null;
|
||||
}
|
||||
|
||||
export const enum ViewState {
|
||||
New = 1,
|
||||
Initialized,
|
||||
Attached,
|
||||
Destroyed
|
||||
}
|
||||
|
||||
export const enum NavDirection {
|
||||
back = 'back',
|
||||
forward = 'forward'
|
||||
}
|
||||
|
||||
export const INIT_ZINDEX = 100;
|
||||
|
||||
export type NavParams = {[key: string]: any};
|
||||
|
||||
export interface NavResult {
|
||||
hasCompleted: boolean;
|
||||
requiresTransition: boolean;
|
||||
enteringName?: string;
|
||||
leavingName?: string;
|
||||
direction?: string;
|
||||
}
|
||||
|
||||
export interface NavOptions {
|
||||
animate?: boolean;
|
||||
animation?: string;
|
||||
direction?: string;
|
||||
duration?: number;
|
||||
easing?: string;
|
||||
id?: string;
|
||||
keyboardClose?: boolean;
|
||||
progressAnimation?: boolean;
|
||||
disableApp?: boolean;
|
||||
minClickBlockDuration?: number;
|
||||
ev?: any;
|
||||
updateUrl?: boolean;
|
||||
isNavRoot?: boolean;
|
||||
viewIsReady?: () => Promise<any>;
|
||||
}
|
||||
|
||||
export interface Page extends Function {
|
||||
new (...args: any[]): any;
|
||||
}
|
||||
|
||||
export interface TransitionResolveFn {
|
||||
(hasCompleted: boolean, requiresTransition: boolean, enteringName?: string, leavingName?: string, direction?: string): void;
|
||||
}
|
||||
|
||||
export interface TransitionRejectFn {
|
||||
(rejectReason: any, transition?: Transition): void;
|
||||
}
|
||||
|
||||
export interface TransitionDoneFn {
|
||||
(hasCompleted: boolean, requiresTransition: boolean, enteringName?: string, leavingName?: string, direction?: string): void;
|
||||
}
|
||||
|
||||
export interface TransitionInstruction {
|
||||
opts: NavOptions;
|
||||
insertStart?: number;
|
||||
insertViews?: any[];
|
||||
removeView?: ViewController;
|
||||
removeStart?: number;
|
||||
removeCount?: number;
|
||||
resolve?: (hasCompleted: boolean) => void;
|
||||
reject?: (rejectReason: string) => void;
|
||||
done?: TransitionDoneFn;
|
||||
leavingRequiresTransition?: boolean;
|
||||
enteringRequiresTransition?: boolean;
|
||||
requiresTransition?: boolean;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Component, Event, Prop } from '@stencil/core';
|
||||
import { EventEmitter } from 'ionicons/dist/types/stencil.core';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-route'
|
||||
})
|
||||
export class Route {
|
||||
|
||||
@Prop() url = '';
|
||||
@Prop() component: string;
|
||||
@Prop() redirectTo: string;
|
||||
@Prop() componentProps: {[key: string]: any};
|
||||
|
||||
@Event() ionRouteDataChanged: EventEmitter;
|
||||
|
||||
componentDidLoad() {
|
||||
this.ionRouteDataChanged.emit();
|
||||
}
|
||||
componentDidUnload() {
|
||||
this.ionRouteDataChanged.emit();
|
||||
}
|
||||
componentDidUpdate() {
|
||||
this.ionRouteDataChanged.emit();
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
import { Build, Component, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core';
|
||||
import { Config, DomController } from '../../index';
|
||||
import { flattenRouterTree, readRedirects, readRoutes } from './utils/parser';
|
||||
import { readNavState, writeNavState } from './utils/dom';
|
||||
import { chainToPath, generatePath, parsePath, readPath, writePath } from './utils/path';
|
||||
import { RouteChain, RouteRedirect, RouterEventDetail } from './utils/interfaces';
|
||||
import { routeRedirect, routerIDsToChain, routerPathToChain } from './utils/matching';
|
||||
import { printRoutes } from './utils/debug';
|
||||
|
||||
|
||||
@Component({
|
||||
tag: 'ion-router'
|
||||
})
|
||||
export class Router {
|
||||
|
||||
private routes: RouteChain[];
|
||||
private previousPath: string = null;
|
||||
private redirects: RouteRedirect[];
|
||||
private busy = false;
|
||||
private init = false;
|
||||
private state = 0;
|
||||
private timer: any;
|
||||
|
||||
@Element() el: HTMLElement;
|
||||
|
||||
@Prop({ context: 'config' }) config: Config;
|
||||
@Prop({ context: 'dom' }) dom: DomController;
|
||||
|
||||
@Prop() base = '';
|
||||
@Prop() useHash = true;
|
||||
|
||||
@Event() ionRouteChanged: EventEmitter<RouterEventDetail>;
|
||||
|
||||
componentDidLoad() {
|
||||
this.init = true;
|
||||
this.onRouteChanged();
|
||||
}
|
||||
|
||||
@Listen('ionRouteDataChanged')
|
||||
protected onRouteChanged() {
|
||||
if (!this.init) {
|
||||
return;
|
||||
}
|
||||
const tree = readRoutes(this.el);
|
||||
this.routes = flattenRouterTree(tree);
|
||||
this.redirects = readRedirects(this.el);
|
||||
|
||||
if (Build.isDev) {
|
||||
printRoutes(this.routes);
|
||||
}
|
||||
|
||||
// schedule write
|
||||
if (this.timer) {
|
||||
cancelAnimationFrame(this.timer);
|
||||
this.timer = undefined;
|
||||
}
|
||||
this.timer = requestAnimationFrame(() => {
|
||||
this.timer = undefined;
|
||||
this.onPopState();
|
||||
});
|
||||
}
|
||||
|
||||
@Listen('window:popstate')
|
||||
protected onPopState() {
|
||||
if (window.history.state === null) {
|
||||
this.state++;
|
||||
window.history.replaceState(this.state, document.title, document.location.href);
|
||||
}
|
||||
this.writeNavStateRoot(this.getPath());
|
||||
}
|
||||
|
||||
@Method()
|
||||
navChanged(isPop: boolean): Promise<boolean> {
|
||||
if (this.busy) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
console.debug('[IN] nav changed -> update URL');
|
||||
const { ids, pivot } = readNavState(document.body);
|
||||
const chain = routerIDsToChain(ids, this.routes);
|
||||
if (!chain) {
|
||||
console.warn('no matching URL for ', ids.map(i => i.id));
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const path = chainToPath(chain);
|
||||
this.setPath(path, isPop);
|
||||
|
||||
const promise = (chain.length > ids.length)
|
||||
? this.writeNavState(pivot, chain.slice(ids.length), 0)
|
||||
: Promise.resolve(true);
|
||||
|
||||
return promise.then(() => {
|
||||
this.emitRouteChange(path, null);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Method()
|
||||
push(url: string, backDirection = false) {
|
||||
const path = parsePath(url);
|
||||
this.setPath(path, backDirection);
|
||||
return this.writeNavStateRoot(path);
|
||||
}
|
||||
|
||||
private writeNavStateRoot(path: string[]): Promise<boolean> {
|
||||
if (this.busy) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
const redirect = routeRedirect(path, this.redirects);
|
||||
let redirectFrom: string[] = null;
|
||||
if (redirect) {
|
||||
this.setPath(redirect.to, true);
|
||||
redirectFrom = redirect.path;
|
||||
path = redirect.to;
|
||||
}
|
||||
const direction = window.history.state >= this.state ? 1 : -1;
|
||||
const chain = routerPathToChain(path, this.routes);
|
||||
return this.writeNavState(document.body, chain, direction).then(changed => {
|
||||
if (changed) {
|
||||
this.emitRouteChange(path, redirectFrom);
|
||||
}
|
||||
return changed;
|
||||
});
|
||||
}
|
||||
|
||||
private writeNavState(node: any, chain: RouteChain, direction: number): Promise<boolean> {
|
||||
if (this.busy) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
this.busy = true;
|
||||
return writeNavState(node, chain, 0, direction).then(changed => {
|
||||
this.busy = false;
|
||||
return changed;
|
||||
});
|
||||
}
|
||||
|
||||
private setPath(path: string[], isPop: boolean) {
|
||||
this.state++;
|
||||
writePath(window.history, this.base, this.useHash, path, isPop, this.state);
|
||||
}
|
||||
|
||||
private getPath(): string[] | null {
|
||||
return readPath(window.location, this.base, this.useHash);
|
||||
}
|
||||
|
||||
private emitRouteChange(path: string[], redirectPath: string[]|null) {
|
||||
const from = this.previousPath;
|
||||
const redirectedFrom = redirectPath ? generatePath(redirectPath) : null;
|
||||
const to = generatePath(path);
|
||||
this.previousPath = to;
|
||||
this.ionRouteChanged.emit({
|
||||
from,
|
||||
redirectedFrom,
|
||||
to: to
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,361 +0,0 @@
|
||||
import { RouteChain } from '../utils/interfaces';
|
||||
import { RouterSegments, matchesIDs, matchesPath, mergeParams, routerPathToChain } from '../utils/matching';
|
||||
import { parsePath } from '../utils/path';
|
||||
|
||||
const CHAIN_1: RouteChain = [
|
||||
{ id: '2', path: ['to'], params: undefined },
|
||||
{ id: '1', path: ['path'], params: undefined },
|
||||
{ id: '3', path: ['segment'], params: undefined },
|
||||
{ id: '4', path: [''], params: undefined },
|
||||
];
|
||||
|
||||
const CHAIN_2: RouteChain = [
|
||||
{ id: '2', path: [''], params: undefined },
|
||||
{ id: '1', path: [''], params: undefined },
|
||||
{ id: '3', path: ['segment', 'to'], params: undefined },
|
||||
{ id: '4', path: [''], params: undefined },
|
||||
{ id: '5', path: ['hola'], params: undefined },
|
||||
{ id: '6', path: [''], params: undefined },
|
||||
{ id: '7', path: [''], params: undefined },
|
||||
{ id: '8', path: ['adios', 'que', 'tal'], params: undefined },
|
||||
];
|
||||
|
||||
const CHAIN_3: RouteChain = [
|
||||
{ id: '2', path: ['this', 'to'], params: undefined },
|
||||
{ id: '1', path: ['path'], params: undefined },
|
||||
{ id: '3', path: ['segment', 'to', 'element'], params: undefined },
|
||||
{ id: '4', path: [''], params: undefined },
|
||||
];
|
||||
|
||||
|
||||
|
||||
describe('matchesIDs', () => {
|
||||
it('should match simple set of ids', () => {
|
||||
const chain: RouteChain = CHAIN_1;
|
||||
expect(matchesIDs(['2'], chain)).toBe(1);
|
||||
expect(matchesIDs(['2', '1'], chain)).toBe(2);
|
||||
expect(matchesIDs(['2', '1', '3'], chain)).toBe(3);
|
||||
expect(matchesIDs(['2', '1', '3', '4'], chain)).toBe(4);
|
||||
expect(matchesIDs(['2', '1', '3', '4', '5'], chain)).toBe(4);
|
||||
|
||||
expect(matchesIDs([], chain)).toBe(0);
|
||||
expect(matchesIDs(['1'], chain)).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('matchesPath', () => {
|
||||
it('should match simple path', () => {
|
||||
const chain: RouteChain = CHAIN_3;
|
||||
expect(matchesPath(['this'], chain)).toEqual(null);
|
||||
expect(matchesPath(['this', 'to'], chain)).toEqual(null);
|
||||
expect(matchesPath(['this', 'to', 'path'], chain)).toEqual(null);
|
||||
expect(matchesPath(['this', 'to', 'path', 'segment'], chain)).toEqual(null);
|
||||
expect(matchesPath(['this', 'to', 'path', 'segment', 'to'], chain)).toEqual(null);
|
||||
expect(matchesPath(['this', 'to', 'path', 'segment', 'to', 'element'], chain)).toEqual(chain);
|
||||
expect(matchesPath(['this', 'to', 'path', 'segment', 'to', 'element', 'more'], chain)).toEqual(null);
|
||||
|
||||
expect(matchesPath([], chain)).toEqual(null);
|
||||
expect(matchesPath([''], chain)).toEqual(null);
|
||||
expect(matchesPath(['path'], chain)).toEqual(null);
|
||||
});
|
||||
|
||||
it('should match simple default route', () => {
|
||||
const chain: RouteChain = CHAIN_2;
|
||||
expect(matchesPath([''], chain)).toEqual(null);
|
||||
expect(matchesPath(['segment'], chain)).toEqual(null);
|
||||
expect(matchesPath(['segment', 'to'], chain)).toEqual(null);
|
||||
expect(matchesPath(['segment', 'to', 'hola'], chain)).toEqual(null);
|
||||
expect(matchesPath(['segment', 'to', 'hola', 'adios'], chain)).toEqual(null);
|
||||
expect(matchesPath(['segment', 'to', 'hola', 'adios', 'que'], chain)).toEqual(null);
|
||||
expect(matchesPath(['segment', 'to', 'hola', 'adios', 'que', 'tal'], chain)).toEqual(chain);
|
||||
expect(matchesPath(['segment', 'to', 'hola', 'adios', 'que', 'tal', 'more'], chain)).toEqual(chain);
|
||||
|
||||
expect(matchesPath(['to'], chain)).toEqual(null);
|
||||
expect(matchesPath(['path', 'to'], chain)).toEqual(null);
|
||||
});
|
||||
|
||||
it('should match simple route 2', () => {
|
||||
const chain: RouteChain = [{ id: '5', path: ['hola'], params: undefined }];
|
||||
expect(matchesPath([''], chain)).toEqual(null);
|
||||
expect(matchesPath(['hola'], chain)).toEqual(chain);
|
||||
expect(matchesPath(['hola', 'hola'], chain)).toEqual(chain);
|
||||
expect(matchesPath(['hola', 'adios'], chain)).toEqual(chain);
|
||||
});
|
||||
|
||||
it('should match simple route 3', () => {
|
||||
const chain: RouteChain = [{ id: '5', path: ['hola', 'adios'], params: undefined }];
|
||||
expect(matchesPath([''], chain)).toEqual(null);
|
||||
expect(matchesPath(['hola'], chain)).toEqual(null);
|
||||
expect(matchesPath(['hola', 'hola'], chain)).toEqual(null);
|
||||
expect(matchesPath(['hola', 'adios'], chain)).toEqual(chain);
|
||||
expect(matchesPath(['hola', 'adios', 'bye'], chain)).toEqual(chain);
|
||||
});
|
||||
|
||||
it('should match simple route 4', () => {
|
||||
const chain: RouteChain = [
|
||||
{ id: '5', path: ['hola'], params: undefined },
|
||||
{ id: '5', path: ['adios'], params: undefined }];
|
||||
|
||||
expect(matchesPath([''], chain)).toEqual(null);
|
||||
expect(matchesPath(['hola'], chain)).toEqual(null);
|
||||
expect(matchesPath(['hola', 'hola'], chain)).toEqual(null);
|
||||
expect(matchesPath(['hola', 'adios'], chain)).toEqual(chain);
|
||||
});
|
||||
|
||||
it('should match with parameters', () => {
|
||||
const chain: RouteChain = [
|
||||
{ id: '5', path: ['profile', ':name'], params: undefined },
|
||||
{ id: '5', path: [''], params: undefined },
|
||||
{ id: '5', path: ['image'], params: {size: 'lg'} },
|
||||
{ id: '5', path: ['image', ':size', ':type'], params: {size: 'mg'} },
|
||||
];
|
||||
const matched = matchesPath(parsePath('/profile/manu/image/image/large/retina'), chain);
|
||||
expect(matched).toEqual([
|
||||
{ id: '5', path: ['profile', ':name'], params: {name: 'manu'} },
|
||||
{ id: '5', path: [''], params: undefined },
|
||||
{ id: '5', path: ['image'], params: {size: 'lg'} },
|
||||
{ id: '5', path: ['image', ':size', ':type'], params: {size: 'large', type: 'retina'} },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('routerPathToChain', () => {
|
||||
it('should match the route with higher priority', () => {
|
||||
const chain3: RouteChain = [{ id: '5', path: ['hola'], params: undefined }];
|
||||
const chain4: RouteChain = [
|
||||
{ id: '5', path: ['hola'], params: undefined },
|
||||
{ id: '5', path: ['adios'], params: undefined }];
|
||||
|
||||
const routes: RouteChain[] = [
|
||||
CHAIN_1,
|
||||
CHAIN_2,
|
||||
chain3,
|
||||
chain4
|
||||
];
|
||||
expect(routerPathToChain(['to'], routes)).toEqual(null);
|
||||
expect(routerPathToChain([''], routes)).toEqual(null);
|
||||
expect(routerPathToChain(['segment', 'to'], routes)).toEqual(null);
|
||||
expect(routerPathToChain(['hola'], routes)).toEqual(chain3);
|
||||
expect(routerPathToChain(['hola', 'hola'], routes)).toEqual(chain3);
|
||||
expect(routerPathToChain(['hola', 'adios'], routes)).toEqual(chain4);
|
||||
});
|
||||
|
||||
it('should match the route with higher priority 2', () => {
|
||||
|
||||
const chain1: RouteChain = [{ id: '1', path: ['categories', ':category_slug'], params: undefined }];
|
||||
const chain2: RouteChain = [{ id: '2', path: ['workouts', ':workout_slug'], params: undefined }];
|
||||
const chain3: RouteChain = [{ id: '3', path: ['workouts', ':workout_slug', 'time-select'], params: undefined }];
|
||||
const chain4: RouteChain = [{ id: '4', path: ['workouts', ':workout_slug', 'end-workout'], params: undefined }];
|
||||
const chain5: RouteChain = [{ id: '5', path: ['plans'], params: undefined }];
|
||||
const chain6: RouteChain = [{ id: '6', path: ['custom'], params: undefined }];
|
||||
const chain7: RouteChain = [{ id: '7', path: ['workouts', 'list'], params: undefined }];
|
||||
|
||||
const routes: RouteChain[] = [
|
||||
chain1,
|
||||
chain2,
|
||||
chain3,
|
||||
chain4,
|
||||
chain5,
|
||||
chain6,
|
||||
chain7
|
||||
];
|
||||
// no match
|
||||
expect(routerPathToChain(['categories'], routes)).toEqual(null);
|
||||
expect(routerPathToChain(['workouts'], routes)).toEqual(null);
|
||||
|
||||
expect(routerPathToChain(['plans'], routes)).toEqual(chain5);
|
||||
expect(routerPathToChain(['custom'], routes)).toEqual(chain6);
|
||||
expect(routerPathToChain(['workouts', 'list'], routes)).toEqual(chain7);
|
||||
|
||||
expect(routerPathToChain(['workouts', 'hola'], routes)).toEqual(
|
||||
[{ id: '2', path: ['workouts', ':workout_slug'], params: {'workout_slug': 'hola'} }]
|
||||
);
|
||||
expect(routerPathToChain(['workouts', 'hello', 'time-select'], routes)).toEqual(
|
||||
[{ id: '3', path: ['workouts', ':workout_slug', 'time-select'], params: {'workout_slug': 'hello'} }]
|
||||
);
|
||||
expect(routerPathToChain(['workouts', 'hello2', 'end-workout'], routes)).toEqual(
|
||||
[{ id: '4', path: ['workouts', ':workout_slug', 'end-workout'], params: {'workout_slug': 'hello2'} }]
|
||||
);
|
||||
});
|
||||
|
||||
it('should match the default route', () => {
|
||||
const chain1: RouteChain = [
|
||||
{ id: 'tabs', path: [''], params: undefined },
|
||||
{ id: 'tab1', path: [''], params: undefined },
|
||||
{ id: 'schedule', path: [''], params: undefined }
|
||||
];
|
||||
const chain2: RouteChain = [
|
||||
{ id: 'tabs', path: [''], params: undefined },
|
||||
{ id: 'tab2', path: ['tab2'], params: undefined },
|
||||
{ id: 'page2', path: [''], params: undefined }
|
||||
];
|
||||
|
||||
expect(routerPathToChain([''], [chain1])).toEqual(chain1);
|
||||
expect(routerPathToChain(['tab2'], [chain1])).toEqual(null);
|
||||
|
||||
expect(routerPathToChain([''], [chain2])).toEqual(null);
|
||||
expect(routerPathToChain(['tab2'], [chain2])).toEqual(chain2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mergeParams', () => {
|
||||
it('should merge undefined', () => {
|
||||
expect(mergeParams(undefined, undefined)).toBeUndefined();
|
||||
expect(mergeParams(null, undefined)).toBeUndefined();
|
||||
expect(mergeParams(undefined, null)).toBeUndefined();
|
||||
expect(mergeParams(null, null)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should merge undefined with params', () => {
|
||||
const params = {data: '1'};
|
||||
expect(mergeParams(undefined, params)).toEqual(params);
|
||||
expect(mergeParams(null, params)).toEqual(params);
|
||||
expect(mergeParams(params, undefined)).toEqual(params);
|
||||
expect(mergeParams(params, null)).toEqual(params);
|
||||
});
|
||||
|
||||
it('should merge params with params', () => {
|
||||
const params1 = {data: '1', data3: 'hello'};
|
||||
const params2 = {data: '2', data2: 'hola'};
|
||||
|
||||
expect(mergeParams(params1, params2)).toEqual({
|
||||
data: '2',
|
||||
data2: 'hola',
|
||||
data3: 'hello'
|
||||
});
|
||||
expect(params1).toEqual({data: '1', data3: 'hello'});
|
||||
expect(params2).toEqual({data: '2', data2: 'hola'});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('RouterSegments', () => {
|
||||
it ('should initialize with empty array', () => {
|
||||
const s = new RouterSegments([]);
|
||||
expect(s.next()).toEqual('');
|
||||
expect(s.next()).toEqual('');
|
||||
expect(s.next()).toEqual('');
|
||||
expect(s.next()).toEqual('');
|
||||
expect(s.next()).toEqual('');
|
||||
});
|
||||
|
||||
it ('should initialize with array', () => {
|
||||
const s = new RouterSegments(['', 'path', 'to', 'destination']);
|
||||
expect(s.next()).toEqual('');
|
||||
expect(s.next()).toEqual('path');
|
||||
expect(s.next()).toEqual('to');
|
||||
expect(s.next()).toEqual('destination');
|
||||
expect(s.next()).toEqual('');
|
||||
expect(s.next()).toEqual('');
|
||||
});
|
||||
});
|
||||
|
||||
// describe('matchRoute', () => {
|
||||
// it('should match simple route', () => {
|
||||
// const path = ['path', 'to', 'component'];
|
||||
// const routes: RouteChain[] = [
|
||||
// [{ id: 2, path: ['to'], params: undefined }],
|
||||
// [{ id: 1, path: ['path'], params: undefined }],
|
||||
// [{ id: 3, path: ['segment'], params: undefined }],
|
||||
// [{ id: 4, path: [''], params: undefined }],
|
||||
// ];
|
||||
// const match = routerPathToChain(path, routes);
|
||||
// expect(match).toEqual({ id: 1, path: ['path'], children: [] });
|
||||
// expect(seg.next()).toEqual('to');
|
||||
// });
|
||||
|
||||
// it('should match default route', () => {
|
||||
// const routes: RouteTree = [
|
||||
// { id: 2, path: ['to'], children: [], params: undefined },
|
||||
// { id: 1, path: ['path'], children: [], params: undefined },
|
||||
// { id: 3, path: ['segment'], children: [], params: undefined },
|
||||
// { id: 4, path: [''], children: [], params: undefined },
|
||||
// ];
|
||||
// const seg = new RouterSegments(['hola', 'path']);
|
||||
// let match = matchRoute(seg, routes);
|
||||
// expect(match).toBeNull();
|
||||
|
||||
// match = matchRoute(seg, routes);
|
||||
// expect(match.id).toEqual(1);
|
||||
|
||||
// for (let i = 0; i < 20; i++) {
|
||||
// match = matchRoute(seg, routes);
|
||||
// expect(match.id).toEqual(4);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('should not match any route', () => {
|
||||
// const routes: RouteTree = [
|
||||
// { id: 2, path: ['to', 'to', 'to'], children: [], params: undefined },
|
||||
// { id: 1, path: ['adam', 'manu'], children: [], params: undefined },
|
||||
// { id: 3, path: ['hola', 'adam'], children: [], params: undefined },
|
||||
// { id: 4, path: [''], children: [], params: undefined },
|
||||
// ];
|
||||
// const seg = new RouterSegments(['hola', 'manu', 'adam']);
|
||||
// const match = matchRoute(seg, routes);
|
||||
// expect(match).toBeNull();
|
||||
// });
|
||||
|
||||
// it('should not match if there are not routes', () => {
|
||||
// const routes: RouteTree = [];
|
||||
// const seg = new RouterSegments(['adam']);
|
||||
// expect(matchRoute(seg, routes)).toBeNull();
|
||||
// expect(matchRoute(seg, routes)).toBeNull();
|
||||
// expect(matchRoute(seg, routes)).toBeNull();
|
||||
// });
|
||||
|
||||
// it('should not match any route (2)', () => {
|
||||
// const routes: RouteTree = [
|
||||
// { id: 1, path: ['adam', 'manu'], children: [], params: undefined },
|
||||
// { id: 3, path: ['hola', 'adam'], children: [], params: undefined },
|
||||
// ];
|
||||
// const seg = new RouterSegments(['adam']);
|
||||
// expect(matchRoute(seg, routes)).toBeNull();
|
||||
// expect(matchRoute(seg, routes)).toBeNull();
|
||||
// expect(matchRoute(seg, routes)).toBeNull();
|
||||
// });
|
||||
|
||||
// it ('should match multiple segments', () => {
|
||||
// const routes: RouteTree = [
|
||||
// { id: 1, path: ['adam', 'manu'], children: [], params: undefined },
|
||||
// { id: 2, path: ['manu', 'hello'], children: [], params: undefined },
|
||||
// { id: 3, path: ['hello'], children: [], params: undefined },
|
||||
// { id: 4, path: [''], children: [], params: undefined },
|
||||
// ];
|
||||
// const seg = new RouterSegments(['adam', 'manu', 'hello', 'manu', 'hello']);
|
||||
// let match = matchRoute(seg, routes);
|
||||
// expect(match.id).toEqual(1);
|
||||
|
||||
// match = matchRoute(seg, routes);
|
||||
// expect(match.id).toEqual(3);
|
||||
|
||||
// match = matchRoute(seg, routes);
|
||||
// expect(match.id).toEqual(2);
|
||||
|
||||
// match = matchRoute(seg, routes);
|
||||
// expect(match.id).toEqual(4);
|
||||
|
||||
// match = matchRoute(seg, routes);
|
||||
// expect(match.id).toEqual(4);
|
||||
// });
|
||||
|
||||
// it('should match long multi segments', () => {
|
||||
// const routes: RouteTree = [
|
||||
// { id: 1, path: ['adam', 'manu', 'hello', 'menu', 'hello'], children: [], params: undefined },
|
||||
// { id: 2, path: ['adam', 'manu', 'hello', 'menu'], children: [], params: undefined },
|
||||
// { id: 3, path: ['adam', 'manu'], children: [], params: undefined },
|
||||
// ];
|
||||
// const seg = new RouterSegments(['adam', 'manu', 'hello', 'menu', 'hello']);
|
||||
// const match = matchRoute(seg, routes);
|
||||
// expect(match.id).toEqual(1);
|
||||
// expect(matchRoute(seg, routes)).toBeNull();
|
||||
// });
|
||||
|
||||
// it('should match long multi segments', () => {
|
||||
// let match = matchRoute(new RouterSegments(['']), null);
|
||||
// expect(match).toBeNull();
|
||||
|
||||
// match = matchRoute(new RouterSegments(['hola']), null);
|
||||
// expect(match).toBeNull();
|
||||
// });
|
||||
// });
|
||||
@@ -1,12 +0,0 @@
|
||||
import { generatePath } from './path';
|
||||
import { RouteChain } from './interfaces';
|
||||
|
||||
export function printRoutes(routes: RouteChain[]) {
|
||||
console.debug('%c[@ionic/core]', 'font-weight: bold', `ion-router registered ${routes.length} routes`);
|
||||
for (const chain of routes) {
|
||||
const path: string[] = [];
|
||||
chain.forEach(r => path.push(...r.path));
|
||||
const ids = chain.map(r => r.id);
|
||||
console.debug(`%c ${generatePath(path)}`, 'font-weight: bold; padding-left: 20px', '=>\t', `(${ids.join(', ')})`);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import { NavOutlet, NavOutletElement, RouteChain, RouteID } from './interfaces';
|
||||
|
||||
export function writeNavState(root: HTMLElement, chain: RouteChain|null, index: number, direction: number): Promise<boolean> {
|
||||
if (!chain || index >= chain.length) {
|
||||
return Promise.resolve(direction === 0);
|
||||
}
|
||||
const route = chain[index];
|
||||
const node = searchNavNode(root);
|
||||
if (!node) {
|
||||
return Promise.resolve(direction === 0);
|
||||
}
|
||||
return node.componentOnReady()
|
||||
.then(() => node.setRouteId(route.id, route.params, direction))
|
||||
.then(result => {
|
||||
if (result.changed) {
|
||||
direction = 0;
|
||||
}
|
||||
const nextEl = node.getContainerEl();
|
||||
const promise = (nextEl)
|
||||
? writeNavState(nextEl, chain, index + 1, direction)
|
||||
: Promise.resolve(direction === 0);
|
||||
|
||||
if (result.markVisible) {
|
||||
return promise.then((c) => {
|
||||
result.markVisible();
|
||||
return c;
|
||||
});
|
||||
}
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
|
||||
export function readNavState(node: HTMLElement) {
|
||||
const ids: RouteID[] = [];
|
||||
let pivot: NavOutlet|null;
|
||||
while (true) {
|
||||
pivot = searchNavNode(node);
|
||||
if (pivot) {
|
||||
const id = pivot.getRouteId();
|
||||
if (id) {
|
||||
node = pivot.getContainerEl();
|
||||
ids.push(id);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {ids, pivot};
|
||||
}
|
||||
|
||||
const QUERY = ':not([no-router]) ion-nav,:not([no-router]) ion-tabs';
|
||||
|
||||
function searchNavNode(root: HTMLElement): NavOutletElement {
|
||||
if (root.matches(QUERY)) {
|
||||
return root as NavOutletElement;
|
||||
}
|
||||
return root.querySelector(QUERY);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
|
||||
export interface NavOutlet {
|
||||
setRouteId(id: string, data: any, direction: number): Promise<RouteWrite>;
|
||||
getRouteId(): RouteID|null;
|
||||
|
||||
getContainerEl(): HTMLElement | null;
|
||||
}
|
||||
|
||||
export interface RouterEventDetail {
|
||||
from: string|null;
|
||||
redirectedFrom: string|null;
|
||||
to: string;
|
||||
}
|
||||
|
||||
export interface RouteRedirect {
|
||||
path: string[];
|
||||
to: string[];
|
||||
}
|
||||
|
||||
export interface RouteWrite {
|
||||
changed: boolean;
|
||||
markVisible?: () => void|Promise<void>;
|
||||
}
|
||||
|
||||
export interface RouteID {
|
||||
id: string;
|
||||
params?: any;
|
||||
}
|
||||
|
||||
export type NavOutletElement = NavOutlet & HTMLStencilElement;
|
||||
|
||||
export interface RouteEntry {
|
||||
id: string;
|
||||
path: string[];
|
||||
params: any|undefined;
|
||||
}
|
||||
|
||||
export interface RouteNode extends RouteEntry {
|
||||
children: RouteTree;
|
||||
}
|
||||
|
||||
export type RouteChain = RouteEntry[];
|
||||
export type RouteTree = RouteNode[];
|
||||
@@ -1,164 +0,0 @@
|
||||
import { RouteChain, RouteID, RouteRedirect } from './interfaces';
|
||||
|
||||
|
||||
export function matchesRedirect(input: string[], route: RouteRedirect): boolean {
|
||||
const {path} = route;
|
||||
if (path.length !== input.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
if (path[i] !== input[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function routeRedirect(path: string[], routes: RouteRedirect[]): RouteRedirect|null {
|
||||
for (const route of routes) {
|
||||
if (matchesRedirect(path, route)) {
|
||||
return route;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
export function matchesIDs(ids: string[], chain: RouteChain): number {
|
||||
const len = Math.min(ids.length, chain.length);
|
||||
let i = 0;
|
||||
for (; i < len; i++) {
|
||||
if (ids[i].toLowerCase() !== chain[i].id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
export function matchesPath(path: string[], chain: RouteChain): RouteChain | null {
|
||||
const segments = new RouterSegments(path);
|
||||
let matchesDefault = false;
|
||||
let allparams: any[];
|
||||
for (let i = 0; i < chain.length; i++) {
|
||||
const path = chain[i].path;
|
||||
if (path[0] === '') {
|
||||
matchesDefault = true;
|
||||
} else {
|
||||
for (const segment of path) {
|
||||
const data = segments.next();
|
||||
// data param
|
||||
if (segment[0] === ':') {
|
||||
if (data === '') {
|
||||
return null;
|
||||
}
|
||||
allparams = allparams || [];
|
||||
const params = allparams[i] || (allparams[i] = {});
|
||||
params[segment.slice(1)] = data;
|
||||
} else if (data !== segment) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
matchesDefault = false;
|
||||
}
|
||||
}
|
||||
const matches = (matchesDefault)
|
||||
? matchesDefault === (segments.next() === '')
|
||||
: true;
|
||||
|
||||
if (!matches) {
|
||||
return null;
|
||||
}
|
||||
if (allparams) {
|
||||
return chain.map((route, i) => ({
|
||||
id: route.id,
|
||||
path: route.path,
|
||||
params: mergeParams(route.params, allparams[i])
|
||||
}));
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
export function mergeParams(a: any, b: any): any {
|
||||
if (!a && b) {
|
||||
return b;
|
||||
} else if (a && !b) {
|
||||
return a;
|
||||
} else if (a && b) {
|
||||
return {
|
||||
...a,
|
||||
...b
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
export function routerIDsToChain(ids: RouteID[], chains: RouteChain[]): RouteChain|null {
|
||||
let match: RouteChain|null = null;
|
||||
let maxMatches = 0;
|
||||
const plainIDs = ids.map(i => i.id);
|
||||
for (const chain of chains) {
|
||||
const score = matchesIDs(plainIDs, chain);
|
||||
if (score > maxMatches) {
|
||||
match = chain;
|
||||
maxMatches = score;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
return match.map((route, i) => ({
|
||||
id: route.id,
|
||||
path: route.path,
|
||||
params: mergeParams(route.params, ids[i] && ids[i].params)
|
||||
}));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
export function routerPathToChain(path: string[], chains: RouteChain[]): RouteChain|null {
|
||||
let match: RouteChain = null;
|
||||
let matches = 0;
|
||||
for (const chain of chains) {
|
||||
const matchedChain = matchesPath(path, chain);
|
||||
if (matchedChain !== null) {
|
||||
const score = computePriority(matchedChain);
|
||||
if (score > matches) {
|
||||
matches = score;
|
||||
match = matchedChain;
|
||||
}
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
export function computePriority(chain: RouteChain): number {
|
||||
let score = 1;
|
||||
let level = 1;
|
||||
for (const route of chain) {
|
||||
for (const path of route.path) {
|
||||
if (path[0] === ':') {
|
||||
score += Math.pow(1, level);
|
||||
} else if (path !== '') {
|
||||
score += Math.pow(2, level);
|
||||
}
|
||||
level++;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
export class RouterSegments {
|
||||
private path: string[];
|
||||
constructor(path: string[]) {
|
||||
this.path = path.slice();
|
||||
}
|
||||
|
||||
next(): string {
|
||||
if (this.path.length > 0) {
|
||||
return this.path.shift() as string;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
export interface FrameworkDelegate {
|
||||
attachViewToDom(container: any, component: any, propsOrDataObj?: any, cssClasses?: string[]): Promise<any>;
|
||||
removeViewFromDom(container: any, component: any): Promise<void>;
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import { EventEmitter } from '@stencil/core';
|
||||
|
||||
export function clamp(min: number, n: number, max: number) {
|
||||
return Math.max(min, Math.min(n, max));
|
||||
}
|
||||
|
||||
export function isDef(v: any): boolean { return v !== undefined && v !== null; }
|
||||
|
||||
export function assert(actual: any, reason: string) {
|
||||
if (!actual) {
|
||||
const message = 'ASSERT: ' + reason;
|
||||
console.error(message);
|
||||
debugger; // tslint:disable-line
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function now(ev: UIEvent) {
|
||||
return ev.timeStamp || Date.now();
|
||||
}
|
||||
|
||||
export function pointerCoord(ev: any): {x: number, y: number} {
|
||||
// get X coordinates for either a mouse click
|
||||
// or a touch depending on the given event
|
||||
if (ev) {
|
||||
const changedTouches = ev.changedTouches;
|
||||
if (changedTouches && changedTouches.length > 0) {
|
||||
const touch = changedTouches[0];
|
||||
return {x: touch.clientX, y: touch.clientY};
|
||||
}
|
||||
if (ev.pageX !== undefined) {
|
||||
return {x: ev.pageX, y: ev.pageY};
|
||||
}
|
||||
}
|
||||
return {x: 0, y: 0};
|
||||
}
|
||||
export type Side = 'left' | 'right' | 'start' | 'end';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* Given a side, return if it should be on the right
|
||||
* based on the value of dir
|
||||
* @param side the side
|
||||
* @param isRTL whether the application dir is rtl
|
||||
* @param defaultRight whether the default side is right
|
||||
*/
|
||||
export function isRightSide(side: Side, defaultRight = false): boolean {
|
||||
const isRTL = document.dir === 'rtl';
|
||||
switch (side) {
|
||||
case 'right': return true;
|
||||
case 'left': return false;
|
||||
case 'end': return !isRTL;
|
||||
case 'start': return isRTL;
|
||||
default: return defaultRight ? !isRTL : isRTL;
|
||||
}
|
||||
}
|
||||
|
||||
export function deferEvent(event: EventEmitter): EventEmitter {
|
||||
return debounceEvent(event, 0);
|
||||
}
|
||||
|
||||
export function debounceEvent(event: EventEmitter, wait: number): EventEmitter {
|
||||
const original = (event as any)._original || event;
|
||||
return {
|
||||
_original: event,
|
||||
emit: debounce(original.emit.bind(original), wait)
|
||||
} as EventEmitter;
|
||||
}
|
||||
|
||||
export function debounce(func: Function, wait = 0) {
|
||||
let timer: number;
|
||||
return (...args: any[]): void => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(func, wait, ...args);
|
||||
};
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
import { EventEmitter } from '@stencil/core';
|
||||
import { Animation, AnimationBuilder, Config, CssClassMap, FrameworkDelegate } from '..';
|
||||
|
||||
let lastId = 1;
|
||||
|
||||
/**
|
||||
* Convert an interface where all the properties are optional to mandatory.
|
||||
*/
|
||||
export type Requires<K extends string> = {
|
||||
[P in K]: any;
|
||||
};
|
||||
|
||||
export function createOverlay<T extends HTMLIonOverlayElement & Requires<keyof B>, B>
|
||||
(element: T, opts: B): Promise<T> {
|
||||
// convert the passed in overlay options into props
|
||||
// that get passed down into the new alert
|
||||
Object.assign(element, opts);
|
||||
|
||||
element.overlayId = lastId++;
|
||||
|
||||
// append the alert element to the document body
|
||||
const appRoot = document.querySelector('ion-app') || document.body;
|
||||
appRoot.appendChild(element);
|
||||
|
||||
return element.componentOnReady();
|
||||
}
|
||||
|
||||
export function dismissOverlay(data: any, role: string|undefined, overlays: OverlayMap, id: number): Promise<void> {
|
||||
id = id >= 0 ? id : getHighestId(overlays);
|
||||
const overlay = overlays.get(id);
|
||||
if (!overlay) {
|
||||
return Promise.reject('overlay does not exist');
|
||||
}
|
||||
return overlay.dismiss(data, role);
|
||||
}
|
||||
|
||||
export function getTopOverlay<T extends HTMLIonOverlayElement>(overlays: OverlayMap): T {
|
||||
return overlays.get(getHighestId(overlays)) as T;
|
||||
}
|
||||
|
||||
export function getHighestId(overlays: OverlayMap) {
|
||||
let minimum = -1;
|
||||
overlays.forEach((_, id) => {
|
||||
if (id > minimum) {
|
||||
minimum = id;
|
||||
}
|
||||
});
|
||||
return minimum;
|
||||
}
|
||||
|
||||
export function removeLastOverlay(overlays: OverlayMap) {
|
||||
const toRemove = getTopOverlay(overlays);
|
||||
return toRemove ? toRemove.dismiss() : Promise.resolve();
|
||||
}
|
||||
|
||||
export function present(
|
||||
overlay: OverlayInterface,
|
||||
name: string,
|
||||
iosEnterAnimation: AnimationBuilder,
|
||||
mdEnterAnimation: AnimationBuilder,
|
||||
opts: any
|
||||
) {
|
||||
if (overlay.presented) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
overlay.presented = true;
|
||||
overlay.willPresent.emit();
|
||||
|
||||
// get the user's animation fn if one was provided
|
||||
const animationBuilder = (overlay.enterAnimation)
|
||||
? overlay.enterAnimation
|
||||
: overlay.config.get(name, overlay.mode === 'ios' ? iosEnterAnimation : mdEnterAnimation);
|
||||
|
||||
return overlayAnimation(overlay, animationBuilder, overlay.el, opts).then(() => {
|
||||
overlay.didPresent.emit();
|
||||
});
|
||||
}
|
||||
|
||||
export function dismiss(
|
||||
overlay: OverlayInterface,
|
||||
data: any|undefined,
|
||||
role: string|undefined,
|
||||
name: string,
|
||||
iosLeaveAnimation: AnimationBuilder,
|
||||
mdLeaveAnimation: AnimationBuilder,
|
||||
opts: any
|
||||
): Promise<void> {
|
||||
if (!overlay.presented) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
overlay.presented = false;
|
||||
|
||||
overlay.willDismiss.emit({data, role});
|
||||
|
||||
const animationBuilder = (overlay.leaveAnimation)
|
||||
? overlay.leaveAnimation
|
||||
: overlay.config.get(name, overlay.mode === 'ios' ? iosLeaveAnimation : mdLeaveAnimation);
|
||||
|
||||
return overlayAnimation(overlay, animationBuilder, overlay.el, opts).then(() => {
|
||||
overlay.didDismiss.emit({data, role});
|
||||
if (overlay.el.parentNode) {
|
||||
overlay.el.parentNode.removeChild(overlay.el);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function overlayAnimation(
|
||||
overlay: OverlayInterface,
|
||||
animationBuilder: AnimationBuilder,
|
||||
baseEl: HTMLElement,
|
||||
opts: any
|
||||
): Promise<void> {
|
||||
if (overlay.animation) {
|
||||
overlay.animation.destroy();
|
||||
overlay.animation = undefined;
|
||||
}
|
||||
return overlay.animationCtrl.create(animationBuilder, baseEl, opts).then(animation => {
|
||||
overlay.animation = animation;
|
||||
if (!overlay.willAnimate) {
|
||||
animation.duration(0);
|
||||
}
|
||||
return animation.playAsync();
|
||||
}).then(animation => {
|
||||
animation.destroy();
|
||||
overlay.animation = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
export function autoFocus(containerEl: HTMLElement): HTMLElement|null {
|
||||
const focusableEls = containerEl.querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]');
|
||||
if (focusableEls.length > 0) {
|
||||
const el = focusableEls[0] as HTMLInputElement;
|
||||
el.focus();
|
||||
return el;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function attachComponent(delegate: FrameworkDelegate, container: Element, component: string|HTMLElement, cssClasses: CssClassMap, data: any): Promise<HTMLElement> {
|
||||
if (delegate) {
|
||||
return delegate.attachViewToDom(container, component, data, Object.keys(cssClasses));
|
||||
}
|
||||
const el = (typeof component === 'string') ? document.createElement(component) : component;
|
||||
Object.assign(el, data);
|
||||
Object.keys(cssClasses).forEach(c => el.classList.add(c));
|
||||
container.appendChild(el);
|
||||
if ((el as any).componentOnReady) {
|
||||
return (el as any).componentOnReady();
|
||||
}
|
||||
return Promise.resolve(el);
|
||||
}
|
||||
|
||||
export function eventMethod(element: HTMLElement, eventName: string, callback: Function): Promise<any> {
|
||||
let resolve: Function;
|
||||
const promise = new Promise(r => resolve = r);
|
||||
onceEvent(element, eventName, (event) => {
|
||||
const detail = event.detail;
|
||||
callback && callback(detail);
|
||||
resolve(detail);
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
export function onceEvent(element: HTMLElement, eventName: string, callback: (ev: CustomEvent) => void) {
|
||||
const handler = (ev: CustomEvent) => {
|
||||
element.removeEventListener(eventName, handler);
|
||||
callback(ev);
|
||||
};
|
||||
element.addEventListener(eventName, handler);
|
||||
}
|
||||
|
||||
|
||||
export function isCancel(role: string): boolean {
|
||||
return role === 'cancel' || role === BACKDROP;
|
||||
}
|
||||
|
||||
|
||||
export interface OverlayEventDetail {
|
||||
data?: any;
|
||||
role?: string;
|
||||
}
|
||||
|
||||
export interface OverlayInterface {
|
||||
mode: string;
|
||||
el: HTMLElement;
|
||||
willAnimate: boolean;
|
||||
config: Config;
|
||||
overlayId: number;
|
||||
presented: boolean;
|
||||
animation: Animation|undefined;
|
||||
animationCtrl: HTMLIonAnimationControllerElement;
|
||||
|
||||
enterAnimation: AnimationBuilder;
|
||||
leaveAnimation: AnimationBuilder;
|
||||
|
||||
didPresent: EventEmitter;
|
||||
willPresent: EventEmitter;
|
||||
willDismiss: EventEmitter;
|
||||
didDismiss: EventEmitter;
|
||||
|
||||
present(): Promise<void>;
|
||||
dismiss(data?: any, role?: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface OverlayController {
|
||||
create(opts?: any): Promise<HTMLElement>;
|
||||
dismiss(data?: any, role?: string, alertId?: number): Promise<void>;
|
||||
getTop(): HTMLElement;
|
||||
}
|
||||
|
||||
export interface HTMLIonOverlayElement extends HTMLStencilElement, OverlayInterface {}
|
||||
|
||||
export type OverlayMap = Map<number, HTMLIonOverlayElement>;
|
||||
|
||||
export const BACKDROP = 'backdrop';
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"lerna": "2.0.0-rc.5",
|
||||
"packages": [
|
||||
"*",
|
||||
"demos/*"
|
||||
"packages/*",
|
||||
"packages/demos/*"
|
||||
],
|
||||
"commands": {
|
||||
"bootstrap": {
|
||||
|
||||
3923
package-lock.json
generated
3923
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"jest": "^20.0.4",
|
||||
"lerna": "2.9.0",
|
||||
"lerna": "^2.0.0-rc.5",
|
||||
"mocha": "^4.0.1",
|
||||
"np": "^2.16.0"
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
16
packages/angular/scripts/README.md
Normal file
16
packages/angular/scripts/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# npm link local development
|
||||
|
||||
`npm link` doesn't work as expected due to the `devDependency` on `@angular/core`. This is the work around...
|
||||
|
||||
npm run build.link ../ionic-conference-app
|
||||
|
||||
When the command above is ran from the `packages/angular` directory, it will build `@ionic/angular` and copy the `dist` directory to the correct location of another local project. In the example above, the end result is that it copies the `dist` directory to `../ionic-conference-app/node_modules/@ionic/angular/dist`. The path given should be relative to the root of this mono repo.
|
||||
|
||||
|
||||
# Deploy
|
||||
|
||||
1. `npm run prepare.deploy`
|
||||
2. Review/update changelog
|
||||
3. Commit updates using the package name and version number as the commit message.
|
||||
4. `npm run deploy`
|
||||
5. :tada:
|
||||
@@ -13,17 +13,15 @@ export { VirtualHeader } from './directives/virtual-header';
|
||||
export { VirtualFooter } from './directives/virtual-footer';
|
||||
|
||||
/* Providers */
|
||||
export { AngularDelegate } from './providers/angular-delegate';
|
||||
export { ActionSheetController } from './providers/action-sheet-controller';
|
||||
export { AlertController } from './providers/alert-controller';
|
||||
export { ActionSheetController, ActionSheetProxy } from './providers/action-sheet-controller';
|
||||
export { AlertController, AlertProxy } from './providers/alert-controller';
|
||||
export { Events } from './providers/events';
|
||||
export { LoadingController } from './providers/loading-controller';
|
||||
export { LoadingController, LoadingProxy } from './providers/loading-controller';
|
||||
export { MenuController } from './providers/menu-controller';
|
||||
export { PickerController } from './providers/picker-controller';
|
||||
export { ModalController } from './providers/modal-controller';
|
||||
export { ModalController, ModalProxy } from './providers/modal-controller';
|
||||
export { Platform } from './providers/platform';
|
||||
export { PopoverController } from './providers/popover-controller';
|
||||
export { ToastController } from './providers/toast-controller';
|
||||
export { PopoverController, PopoverProxy } from './providers/popover-controller';
|
||||
export { ToastController, ToastProxy } from './providers/toast-controller';
|
||||
|
||||
export * from './types/interfaces';
|
||||
|
||||
@@ -25,14 +25,12 @@ import { VirtualHeader } from './directives/virtual-header';
|
||||
import { VirtualFooter } from './directives/virtual-footer';
|
||||
|
||||
/* Providers */
|
||||
import { AngularDelegate } from './providers/angular-delegate';
|
||||
import { ActionSheetController } from './providers/action-sheet-controller';
|
||||
import { AlertController } from './providers/alert-controller';
|
||||
import { Events, setupProvideEvents } from './providers/events';
|
||||
import { LoadingController } from './providers/loading-controller';
|
||||
import { MenuController } from './providers/menu-controller';
|
||||
import { ModalController } from './providers/modal-controller';
|
||||
import { PickerController } from './providers/picker-controller';
|
||||
import { Platform } from './providers/platform';
|
||||
import { PopoverController } from './providers/popover-controller';
|
||||
import { ToastController } from './providers/toast-controller';
|
||||
@@ -69,14 +67,13 @@ import { ToastController } from './providers/toast-controller';
|
||||
VirtualHeader,
|
||||
VirtualFooter
|
||||
],
|
||||
providers: [
|
||||
AngularDelegate,
|
||||
ModalController,
|
||||
PopoverController,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
],
|
||||
providers: [
|
||||
ModalController,
|
||||
PopoverController
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
]
|
||||
@@ -90,7 +87,6 @@ export class IonicAngularModule {
|
||||
ActionSheetController,
|
||||
Events,
|
||||
LoadingController,
|
||||
PickerController,
|
||||
MenuController,
|
||||
Platform,
|
||||
ToastController,
|
||||
104
packages/angular/src/providers/action-sheet-controller.ts
Normal file
104
packages/angular/src/providers/action-sheet-controller.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActionSheetDismissEvent, ActionSheetOptions } from '@ionic/core';
|
||||
|
||||
import { ensureElementInBody, hydrateElement } from '../util/util';
|
||||
|
||||
let actionSheetId = 0;
|
||||
|
||||
@Injectable()
|
||||
export class ActionSheetController {
|
||||
create(opts?: ActionSheetOptions): ActionSheetProxy {
|
||||
return getActionSheetProxy(opts);
|
||||
}
|
||||
}
|
||||
|
||||
export function getActionSheetProxy(opts: ActionSheetOptions) {
|
||||
return {
|
||||
id: actionSheetId++,
|
||||
state: PRESENTING,
|
||||
opts: opts,
|
||||
present: function() { return present(this); },
|
||||
dismiss: function() { return dismiss(this); },
|
||||
onDidDismiss: function(callback: (data: any, role: string) => void) {
|
||||
(this as ActionSheetProxyInternal).onDidDismissHandler = callback;
|
||||
},
|
||||
onWillDismiss: function(callback: (data: any, role: string) => void) {
|
||||
(this as ActionSheetProxyInternal).onWillDismissHandler = callback;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function present(actionSheetProxy: ActionSheetProxyInternal): Promise<any> {
|
||||
actionSheetProxy.state = PRESENTING;
|
||||
return loadOverlay(actionSheetProxy.opts).then((actionSheetElement: HTMLIonActionSheetElement) => {
|
||||
actionSheetProxy.element = actionSheetElement;
|
||||
|
||||
const onDidDismissHandler = (event: ActionSheetDismissEvent) => {
|
||||
actionSheetElement.removeEventListener(ION_ACTION_SHEET_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||
if (actionSheetProxy.onDidDismissHandler) {
|
||||
actionSheetProxy.onDidDismissHandler(event.detail.data, event.detail.role);
|
||||
}
|
||||
};
|
||||
|
||||
const onWillDismissHandler = (event: ActionSheetDismissEvent) => {
|
||||
actionSheetElement.removeEventListener(ION_ACTION_SHEET_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||
if (actionSheetProxy.onWillDismissHandler) {
|
||||
actionSheetProxy.onWillDismissHandler(event.detail.data, event.detail.role);
|
||||
}
|
||||
};
|
||||
|
||||
actionSheetElement.addEventListener(ION_ACTION_SHEET_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||
actionSheetElement.addEventListener(ION_ACTION_SHEET_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||
|
||||
if (actionSheetProxy.state === PRESENTING) {
|
||||
return actionSheetElement.present();
|
||||
}
|
||||
|
||||
// we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state
|
||||
// attribute before it could async load and present itself.
|
||||
// with that in mind, just return null to make the TS compiler happy
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
export function dismiss(actionSheetProxy: ActionSheetProxyInternal): Promise<any> {
|
||||
actionSheetProxy.state = DISMISSING;
|
||||
if (actionSheetProxy.element) {
|
||||
if (actionSheetProxy.state === DISMISSING) {
|
||||
return actionSheetProxy.element.dismiss();
|
||||
}
|
||||
}
|
||||
// either we're not in the dismissing state
|
||||
// or we're calling this before the element is created
|
||||
// so just return a resolved promise
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function loadOverlay(opts: ActionSheetOptions): Promise<HTMLIonActionSheetElement> {
|
||||
const element = ensureElementInBody('ion-action-sheet-controller') as HTMLIonActionSheetControllerElement;
|
||||
return hydrateElement(element).then(() => {
|
||||
return element.create(opts);
|
||||
});
|
||||
}
|
||||
|
||||
export interface ActionSheetProxy {
|
||||
present(): Promise<void>;
|
||||
dismiss(): Promise<void>;
|
||||
onDidDismiss(callback: (data: any, role: string) => void): void;
|
||||
onWillDismiss(callback: (data: any, role: string) => void): void;
|
||||
}
|
||||
|
||||
export interface ActionSheetProxyInternal extends ActionSheetProxy {
|
||||
id: number;
|
||||
opts: ActionSheetOptions;
|
||||
state: number;
|
||||
element: HTMLIonActionSheetElement;
|
||||
onDidDismissHandler?: (data: any, role: string) => void;
|
||||
onWillDismissHandler?: (data: any, role: string) => void;
|
||||
}
|
||||
|
||||
export const PRESENTING = 1;
|
||||
export const DISMISSING = 2;
|
||||
|
||||
const ION_ACTION_SHEET_DID_DISMISS_EVENT = 'ionActionSheetDidDismiss';
|
||||
const ION_ACTION_SHEET_WILL_DISMISS_EVENT = 'ionActionSheetWillDismiss';
|
||||
104
packages/angular/src/providers/alert-controller.ts
Normal file
104
packages/angular/src/providers/alert-controller.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AlertDismissEvent, AlertOptions } from '@ionic/core';
|
||||
|
||||
import { ensureElementInBody, hydrateElement } from '../util/util';
|
||||
|
||||
let alertId = 0;
|
||||
|
||||
@Injectable()
|
||||
export class AlertController {
|
||||
create(opts?: AlertOptions): AlertProxy {
|
||||
return getAlertProxy(opts);
|
||||
}
|
||||
}
|
||||
|
||||
export function getAlertProxy(opts: AlertOptions) {
|
||||
return {
|
||||
id: alertId++,
|
||||
state: PRESENTING,
|
||||
opts: opts,
|
||||
present: function() { return present(this); },
|
||||
dismiss: function() { return dismiss(this); },
|
||||
onDidDismiss: function(callback: (data: any, role: string) => void) {
|
||||
(this as AlertProxyInternal).onDidDismissHandler = callback;
|
||||
},
|
||||
onWillDismiss: function(callback: (data: any, role: string) => void) {
|
||||
(this as AlertProxyInternal).onWillDismissHandler = callback;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function present(alertProxy: AlertProxyInternal): Promise<any> {
|
||||
alertProxy.state = PRESENTING;
|
||||
return loadOverlay(alertProxy.opts).then((alertElement: HTMLIonAlertElement) => {
|
||||
alertProxy.element = alertElement;
|
||||
|
||||
const onDidDismissHandler = (event: AlertDismissEvent) => {
|
||||
alertElement.removeEventListener(ION_ALERT_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||
if (alertProxy.onDidDismissHandler) {
|
||||
alertProxy.onDidDismissHandler(event.detail.data, event.detail.role);
|
||||
}
|
||||
};
|
||||
|
||||
const onWillDismissHandler = (event: AlertDismissEvent) => {
|
||||
alertElement.removeEventListener(ION_ALERT_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||
if (alertProxy.onWillDismissHandler) {
|
||||
alertProxy.onWillDismissHandler(event.detail.data, event.detail.role);
|
||||
}
|
||||
};
|
||||
|
||||
alertElement.addEventListener(ION_ALERT_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||
alertElement.addEventListener(ION_ALERT_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||
|
||||
if (alertProxy.state === PRESENTING) {
|
||||
return alertElement.present();
|
||||
}
|
||||
|
||||
// we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state
|
||||
// attribute before it could async load and present itself.
|
||||
// with that in mind, just return null to make the TS compiler happy
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
export function dismiss(alertProxy: AlertProxyInternal): Promise<any> {
|
||||
alertProxy.state = DISMISSING;
|
||||
if (alertProxy.element) {
|
||||
if (alertProxy.state === DISMISSING) {
|
||||
return alertProxy.element.dismiss();
|
||||
}
|
||||
}
|
||||
// either we're not in the dismissing state
|
||||
// or we're calling this before the element is created
|
||||
// so just return a resolved promise
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function loadOverlay(opts: AlertOptions): Promise<HTMLIonAlertElement> {
|
||||
const element = ensureElementInBody('ion-alert-controller') as HTMLIonAlertControllerElement;
|
||||
return hydrateElement(element).then(() => {
|
||||
return element.create(opts);
|
||||
});
|
||||
}
|
||||
|
||||
export interface AlertProxy {
|
||||
present(): Promise<void>;
|
||||
dismiss(): Promise<void>;
|
||||
onDidDismiss(callback: (data: any, role: string) => void): void;
|
||||
onWillDismiss(callback: (data: any, role: string) => void): void;
|
||||
}
|
||||
|
||||
export interface AlertProxyInternal extends AlertProxy {
|
||||
id: number;
|
||||
opts: AlertOptions;
|
||||
state: number;
|
||||
element: HTMLIonAlertElement;
|
||||
onDidDismissHandler?: (data: any, role: string) => void;
|
||||
onWillDismissHandler?: (data: any, role: string) => void;
|
||||
}
|
||||
|
||||
export const PRESENTING = 1;
|
||||
export const DISMISSING = 2;
|
||||
|
||||
const ION_ALERT_DID_DISMISS_EVENT = 'ionAlertDidDismiss';
|
||||
const ION_ALERT_WILL_DISMISS_EVENT = 'ionAlertWillDismiss';
|
||||
104
packages/angular/src/providers/loading-controller.ts
Normal file
104
packages/angular/src/providers/loading-controller.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { LoadingDismissEvent, LoadingOptions } from '@ionic/core';
|
||||
|
||||
import { ensureElementInBody, hydrateElement } from '../util/util';
|
||||
|
||||
let loadingId = 0;
|
||||
|
||||
@Injectable()
|
||||
export class LoadingController {
|
||||
create(opts?: LoadingOptions): LoadingProxy {
|
||||
return getLoadingProxy(opts);
|
||||
}
|
||||
}
|
||||
|
||||
export function getLoadingProxy(opts: LoadingOptions) {
|
||||
return {
|
||||
id: loadingId++,
|
||||
state: PRESENTING,
|
||||
opts: opts,
|
||||
present: function() { return present(this); },
|
||||
dismiss: function() { return dismiss(this); },
|
||||
onDidDismiss: function(callback: (data: any, role: string) => void) {
|
||||
(this as LoadingProxyInternal).onDidDismissHandler = callback;
|
||||
},
|
||||
onWillDismiss: function(callback: (data: any, role: string) => void) {
|
||||
(this as LoadingProxyInternal).onWillDismissHandler = callback;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function present(loadingProxy: LoadingProxyInternal): Promise<any> {
|
||||
loadingProxy.state = PRESENTING;
|
||||
return loadOverlay(loadingProxy.opts).then((loadingElement: HTMLIonLoadingElement) => {
|
||||
loadingProxy.element = loadingElement;
|
||||
|
||||
const onDidDismissHandler = (event: LoadingDismissEvent) => {
|
||||
loadingElement.removeEventListener(ION_LOADING_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||
if (loadingProxy.onDidDismissHandler) {
|
||||
loadingProxy.onDidDismissHandler(event.detail.data, event.detail.role);
|
||||
}
|
||||
};
|
||||
|
||||
const onWillDismissHandler = (event: LoadingDismissEvent) => {
|
||||
loadingElement.removeEventListener(ION_LOADING_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||
if (loadingProxy.onWillDismissHandler) {
|
||||
loadingProxy.onWillDismissHandler(event.detail.data, event.detail.role);
|
||||
}
|
||||
};
|
||||
|
||||
loadingElement.addEventListener(ION_LOADING_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||
loadingElement.addEventListener(ION_LOADING_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||
|
||||
if (loadingProxy.state === PRESENTING) {
|
||||
return loadingElement.present();
|
||||
}
|
||||
|
||||
// we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state
|
||||
// attribute before it could async load and present itself.
|
||||
// with that in mind, just return null to make the TS compiler happy
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
export function dismiss(loadingProxy: LoadingProxyInternal): Promise<any> {
|
||||
loadingProxy.state = DISMISSING;
|
||||
if (loadingProxy.element) {
|
||||
if (loadingProxy.state === DISMISSING) {
|
||||
return loadingProxy.element.dismiss();
|
||||
}
|
||||
}
|
||||
// either we're not in the dismissing state
|
||||
// or we're calling this before the element is created
|
||||
// so just return a resolved promise
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function loadOverlay(opts: LoadingOptions): Promise<HTMLIonLoadingElement> {
|
||||
const element = ensureElementInBody('ion-loading-controller') as HTMLIonLoadingControllerElement;
|
||||
return hydrateElement(element).then(() => {
|
||||
return element.create(opts);
|
||||
});
|
||||
}
|
||||
|
||||
export interface LoadingProxy {
|
||||
present(): Promise<void>;
|
||||
dismiss(): Promise<void>;
|
||||
onDidDismiss(callback: (data: any, role: string) => void): void;
|
||||
onWillDismiss(callback: (data: any, role: string) => void): void;
|
||||
}
|
||||
|
||||
export interface LoadingProxyInternal extends LoadingProxy {
|
||||
id: number;
|
||||
opts: LoadingOptions;
|
||||
state: number;
|
||||
element: HTMLIonLoadingElement;
|
||||
onDidDismissHandler?: (data: any, role: string) => void;
|
||||
onWillDismissHandler?: (data: any, role: string) => void;
|
||||
}
|
||||
|
||||
export const PRESENTING = 1;
|
||||
export const DISMISSING = 2;
|
||||
|
||||
const ION_LOADING_DID_DISMISS_EVENT = 'ionLoadingDidDismiss';
|
||||
const ION_LOADING_WILL_DISMISS_EVENT = 'ionLoadingWillDismiss';
|
||||
127
packages/angular/src/providers/menu-controller.ts
Normal file
127
packages/angular/src/providers/menu-controller.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { ensureElementInBody } from '../util/util';
|
||||
|
||||
let element: HTMLIonMenuControllerElement;
|
||||
export class MenuController {
|
||||
|
||||
|
||||
constructor() {
|
||||
element = ensureElementInBody('ion-menu-controller') as HTMLIonMenuControllerElement;
|
||||
}
|
||||
|
||||
close(menuId?: string) {
|
||||
return element.componentOnReady().then(() => {
|
||||
return element.close(menuId);
|
||||
});
|
||||
}
|
||||
|
||||
// maintain legacy sync api
|
||||
enable(enabled: boolean, menuId?: string) {
|
||||
if (element && element.enable) {
|
||||
return element.enable(enabled, menuId);
|
||||
}
|
||||
// IDK, this is not a good place to be in
|
||||
return null;
|
||||
}
|
||||
|
||||
enableAsync(menuId?: string): Promise<HTMLIonMenuElement> {
|
||||
return element.componentOnReady().then(() => {
|
||||
return element.enable(true, menuId);
|
||||
});
|
||||
}
|
||||
|
||||
get(menuId?: string) {
|
||||
if (element && element.get) {
|
||||
return element.get(menuId);
|
||||
}
|
||||
// IDK, this is not a good place to be in
|
||||
return null;
|
||||
}
|
||||
|
||||
getAsync(menuId?: string): Promise<HTMLIonMenuElement> {
|
||||
return element.componentOnReady().then(() => {
|
||||
return element.get(menuId);
|
||||
});
|
||||
}
|
||||
|
||||
getMenus() {
|
||||
if (element && element.getMenus) {
|
||||
return element.getMenus();
|
||||
}
|
||||
// IDK, this is not a good place to be in
|
||||
return [];
|
||||
}
|
||||
|
||||
getMenusAsync(): Promise<HTMLIonMenuElement[]> {
|
||||
return element.componentOnReady().then(() => {
|
||||
return element.getMenus();
|
||||
});
|
||||
}
|
||||
|
||||
getOpen() {
|
||||
if (element && element.getOpen) {
|
||||
return element.getOpen();
|
||||
}
|
||||
// IDK, this is not a good place to be in
|
||||
return null;
|
||||
}
|
||||
|
||||
getOpenAsync(): Promise<HTMLIonMenuElement> {
|
||||
return element.componentOnReady().then(() => {
|
||||
return element.getOpen();
|
||||
});
|
||||
}
|
||||
|
||||
isEnabled(menuId?: string) {
|
||||
if (element && element.isEnabled) {
|
||||
return element.isEnabled(menuId);
|
||||
}
|
||||
// IDK, this is not a good place to be in
|
||||
return false;
|
||||
}
|
||||
|
||||
isEnabledAsync(menuId?: string): Promise<boolean> {
|
||||
return element.componentOnReady().then(() => {
|
||||
return element.isEnabled(menuId);
|
||||
});
|
||||
}
|
||||
|
||||
isOpen(menuId?: string) {
|
||||
if (element && element.isOpen) {
|
||||
return element.isOpen(menuId);
|
||||
}
|
||||
// IDK, this is not a good place to be in
|
||||
return false;
|
||||
}
|
||||
|
||||
isOpenAsync(menuId?: string): Promise<boolean> {
|
||||
return element.componentOnReady().then(() => {
|
||||
return element.isOpen(menuId);
|
||||
});
|
||||
}
|
||||
|
||||
open(menuId?: string): Promise<boolean> {
|
||||
return element.componentOnReady().then(() => {
|
||||
return element.open(menuId);
|
||||
});
|
||||
}
|
||||
|
||||
swipeEnable(shouldEnable: boolean, menuId?: string) {
|
||||
if (element && element.swipeEnable) {
|
||||
return element.swipeEnable(shouldEnable, menuId);
|
||||
}
|
||||
// IDK, this is not a good place to be in
|
||||
return null;
|
||||
}
|
||||
|
||||
swipeEnableAsync(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement> {
|
||||
return element.componentOnReady().then(() => {
|
||||
return element.swipeEnable(shouldEnable, menuId);
|
||||
});
|
||||
}
|
||||
|
||||
toggle(menuId?: string): Promise<boolean> {
|
||||
return element.componentOnReady().then(() => {
|
||||
return element.toggle(menuId);
|
||||
});
|
||||
}
|
||||
}
|
||||
121
packages/angular/src/providers/modal-controller.ts
Normal file
121
packages/angular/src/providers/modal-controller.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import {
|
||||
Injectable,
|
||||
} from '@angular/core';
|
||||
|
||||
import {
|
||||
ModalDismissEvent,
|
||||
ModalOptions
|
||||
} from '@ionic/core';
|
||||
|
||||
import { ensureElementInBody, hydrateElement } from '../util/util';
|
||||
|
||||
let modalId = 0;
|
||||
|
||||
@Injectable()
|
||||
export class ModalController {
|
||||
|
||||
create(opts?: ModalOptions): ModalProxy {
|
||||
return getModalProxy(opts);
|
||||
}
|
||||
|
||||
dismiss(data?: any, role?: string, id?: number) {
|
||||
const modalController = document.querySelector('ion-modal-controller');
|
||||
return modalController.componentOnReady().then(() => {
|
||||
return modalController.dismiss(data, role, id);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function getModalProxy(opts: ModalOptions) {
|
||||
return {
|
||||
id: modalId++,
|
||||
state: PRESENTING,
|
||||
opts: opts,
|
||||
present: function() { return present(this); },
|
||||
dismiss: function() { return dismiss(this); },
|
||||
onDidDismiss: function(callback: (data: any, role: string) => void) {
|
||||
(this as ModalProxyInternal).onDidDismissHandler = callback;
|
||||
},
|
||||
onWillDismiss: function(callback: (data: any, role: string) => void) {
|
||||
(this as ModalProxyInternal).onWillDismissHandler = callback;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function present(modalProxy: ModalProxyInternal): Promise<any> {
|
||||
modalProxy.state = PRESENTING;
|
||||
return loadOverlay(modalProxy.opts).then((modalElement: HTMLIonModalElement) => {
|
||||
Object.assign(modalElement, modalProxy.opts);
|
||||
modalProxy.element = modalElement;
|
||||
|
||||
const onDidDismissHandler = (event: ModalDismissEvent) => {
|
||||
modalElement.removeEventListener(ION_MODAL_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||
if (modalProxy.onDidDismissHandler) {
|
||||
modalProxy.onDidDismissHandler(event.detail.data, event.detail.role);
|
||||
}
|
||||
};
|
||||
|
||||
const onWillDismissHandler = (event: ModalDismissEvent) => {
|
||||
modalElement.removeEventListener(ION_MODAL_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||
if (modalProxy.onWillDismissHandler) {
|
||||
modalProxy.onWillDismissHandler(event.detail.data, event.detail.role);
|
||||
}
|
||||
};
|
||||
|
||||
modalElement.addEventListener(ION_MODAL_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||
modalElement.addEventListener(ION_MODAL_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||
|
||||
if (modalProxy.state === PRESENTING) {
|
||||
return modalElement.present();
|
||||
}
|
||||
|
||||
// we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state
|
||||
// attribute before it could async load and present itself.
|
||||
// with that in mind, just return null to make the TS compiler happy
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
export function dismiss(modalProxy: ModalProxyInternal): Promise<any> {
|
||||
modalProxy.state = DISMISSING;
|
||||
if (modalProxy.element) {
|
||||
if (modalProxy.state === DISMISSING) {
|
||||
return modalProxy.element.dismiss();
|
||||
}
|
||||
}
|
||||
// either we're not in the dismissing state
|
||||
// or we're calling this before the element is created
|
||||
// so just return a resolved promise
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function loadOverlay(opts: ModalOptions): Promise<HTMLIonModalElement> {
|
||||
const element = ensureElementInBody('ion-modal-controller') as HTMLIonModalControllerElement;
|
||||
return hydrateElement(element).then(() => {
|
||||
return element.create(opts);
|
||||
});
|
||||
}
|
||||
|
||||
export interface ModalProxy {
|
||||
present(): Promise<void>;
|
||||
dismiss(): Promise<void>;
|
||||
onDidDismiss(callback: (data: any, role: string) => void): void;
|
||||
onWillDismiss(callback: (data: any, role: string) => void): void;
|
||||
}
|
||||
|
||||
export interface ModalProxyInternal extends ModalProxy {
|
||||
id: number;
|
||||
opts: ModalOptions;
|
||||
state: number;
|
||||
element: HTMLIonModalElement;
|
||||
onDidDismissHandler?: (data: any, role: string) => void;
|
||||
onWillDismissHandler?: (data: any, role: string) => void;
|
||||
}
|
||||
|
||||
export const PRESENTING = 1;
|
||||
export const DISMISSING = 2;
|
||||
|
||||
const ION_MODAL_DID_DISMISS_EVENT = 'ionModalDidDismiss';
|
||||
const ION_MODAL_WILL_DISMISS_EVENT = 'ionModalWillDismiss';
|
||||
|
||||
120
packages/angular/src/providers/popover-controller.ts
Normal file
120
packages/angular/src/providers/popover-controller.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import {
|
||||
Injectable,
|
||||
} from '@angular/core';
|
||||
|
||||
import {
|
||||
PopoverDismissEvent,
|
||||
PopoverOptions
|
||||
} from '@ionic/core';
|
||||
|
||||
import { ensureElementInBody, hydrateElement } from '../util/util';
|
||||
|
||||
let popoverId = 0;
|
||||
|
||||
@Injectable()
|
||||
export class PopoverController {
|
||||
|
||||
create(opts?: PopoverOptions): PopoverProxy {
|
||||
return getPopoverProxy(opts);
|
||||
}
|
||||
|
||||
dismiss(data?: any, role?: string, id?: number) {
|
||||
const popoverController = document.querySelector('ion-popover-controller');
|
||||
return popoverController.componentOnReady().then(() => {
|
||||
return popoverController.dismiss(data, role, id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function getPopoverProxy(opts: PopoverOptions) {
|
||||
return {
|
||||
id: popoverId++,
|
||||
state: PRESENTING,
|
||||
opts: opts,
|
||||
present: function() { return present(this); },
|
||||
dismiss: function() { return dismiss(this); },
|
||||
onDidDismiss: function(callback: (data: any, role: string) => void) {
|
||||
(this as PopoverProxyInternal).onDidDismissHandler = callback;
|
||||
},
|
||||
onWillDismiss: function(callback: (data: any, role: string) => void) {
|
||||
(this as PopoverProxyInternal).onWillDismissHandler = callback;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function present(popoverProxy: PopoverProxyInternal): Promise<any> {
|
||||
popoverProxy.state = PRESENTING;
|
||||
return loadOverlay(popoverProxy.opts).then((popoverElement: HTMLIonPopoverElement) => {
|
||||
Object.assign(popoverElement, popoverProxy.opts);
|
||||
popoverProxy.element = popoverElement;
|
||||
|
||||
const onDidDismissHandler = (event: PopoverDismissEvent) => {
|
||||
popoverElement.removeEventListener(ION_POPOVER_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||
if (popoverProxy.onDidDismissHandler) {
|
||||
popoverProxy.onDidDismissHandler(event.detail.data, event.detail.role);
|
||||
}
|
||||
};
|
||||
|
||||
const onWillDismissHandler = (event: PopoverDismissEvent) => {
|
||||
popoverElement.removeEventListener(ION_POPOVER_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||
if (popoverProxy.onWillDismissHandler) {
|
||||
popoverProxy.onWillDismissHandler(event.detail.data, event.detail.role);
|
||||
}
|
||||
};
|
||||
|
||||
popoverElement.addEventListener(ION_POPOVER_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||
popoverElement.addEventListener(ION_POPOVER_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||
|
||||
if (popoverProxy.state === PRESENTING) {
|
||||
return popoverElement.present();
|
||||
}
|
||||
|
||||
// we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state
|
||||
// attribute before it could async load and present itself.
|
||||
// with that in mind, just return null to make the TS compiler happy
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
export function dismiss(popoverProxy: PopoverProxyInternal): Promise<any> {
|
||||
popoverProxy.state = DISMISSING;
|
||||
if (popoverProxy.element) {
|
||||
if (popoverProxy.state === DISMISSING) {
|
||||
return popoverProxy.element.dismiss();
|
||||
}
|
||||
}
|
||||
// either we're not in the dismissing state
|
||||
// or we're calling this before the element is created
|
||||
// so just return a resolved promise
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function loadOverlay(opts: PopoverOptions): Promise<HTMLIonPopoverElement> {
|
||||
const element = ensureElementInBody('ion-popover-controller') as HTMLIonPopoverControllerElement;
|
||||
return hydrateElement(element).then(() => {
|
||||
return element.create(opts);
|
||||
});
|
||||
}
|
||||
|
||||
export interface PopoverProxy {
|
||||
present(): Promise<void>;
|
||||
dismiss(): Promise<void>;
|
||||
onDidDismiss(callback: (data: any, role: string) => void): void;
|
||||
onWillDismiss(callback: (data: any, role: string) => void): void;
|
||||
}
|
||||
|
||||
export interface PopoverProxyInternal extends PopoverProxy {
|
||||
id: number;
|
||||
opts: PopoverOptions;
|
||||
state: number;
|
||||
element: HTMLIonPopoverElement;
|
||||
onDidDismissHandler?: (data: any, role: string) => void;
|
||||
onWillDismissHandler?: (data: any, role: string) => void;
|
||||
}
|
||||
|
||||
export const PRESENTING = 1;
|
||||
export const DISMISSING = 2;
|
||||
|
||||
const ION_POPOVER_DID_DISMISS_EVENT = 'ionPopoverDidDismiss';
|
||||
const ION_POPOVER_WILL_DISMISS_EVENT = 'ionPopoverWillDismiss';
|
||||
|
||||
104
packages/angular/src/providers/toast-controller.ts
Normal file
104
packages/angular/src/providers/toast-controller.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ToastDismissEvent, ToastOptions } from '@ionic/core';
|
||||
|
||||
import { ensureElementInBody, hydrateElement } from '../util/util';
|
||||
|
||||
let toastId = 0;
|
||||
|
||||
@Injectable()
|
||||
export class ToastController {
|
||||
create(opts?: ToastOptions): ToastProxy {
|
||||
return getToastProxy(opts);
|
||||
}
|
||||
}
|
||||
|
||||
export function getToastProxy(opts: ToastOptions) {
|
||||
return {
|
||||
id: toastId++,
|
||||
state: PRESENTING,
|
||||
opts: opts,
|
||||
present: function() { return present(this); },
|
||||
dismiss: function() { return dismiss(this); },
|
||||
onDidDismiss: function(callback: (data: any, role: string) => void) {
|
||||
(this as ToastProxyInternal).onDidDismissHandler = callback;
|
||||
},
|
||||
onWillDismiss: function(callback: (data: any, role: string) => void) {
|
||||
(this as ToastProxyInternal).onWillDismissHandler = callback;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function present(toastProxy: ToastProxyInternal): Promise<any> {
|
||||
toastProxy.state = PRESENTING;
|
||||
return loadOverlay(toastProxy.opts).then((toastElement: HTMLIonToastElement) => {
|
||||
toastProxy.element = toastElement;
|
||||
|
||||
const onDidDismissHandler = (event: ToastDismissEvent) => {
|
||||
toastElement.removeEventListener(ION_TOAST_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||
if (toastProxy.onDidDismissHandler) {
|
||||
toastProxy.onDidDismissHandler(event.detail.data, event.detail.role);
|
||||
}
|
||||
};
|
||||
|
||||
const onWillDismissHandler = (event: ToastDismissEvent) => {
|
||||
toastElement.removeEventListener(ION_TOAST_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||
if (toastProxy.onWillDismissHandler) {
|
||||
toastProxy.onWillDismissHandler(event.detail.data, event.detail.role);
|
||||
}
|
||||
};
|
||||
|
||||
toastElement.addEventListener(ION_TOAST_DID_DISMISS_EVENT, onDidDismissHandler);
|
||||
toastElement.addEventListener(ION_TOAST_WILL_DISMISS_EVENT, onWillDismissHandler);
|
||||
|
||||
if (toastProxy.state === PRESENTING) {
|
||||
return toastElement.present();
|
||||
}
|
||||
|
||||
// we'll only ever get here if someone tried to dismiss the overlay or mess with it's internal state
|
||||
// attribute before it could async load and present itself.
|
||||
// with that in mind, just return null to make the TS compiler happy
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
export function dismiss(toastProxy: ToastProxyInternal): Promise<any> {
|
||||
toastProxy.state = DISMISSING;
|
||||
if (toastProxy.element) {
|
||||
if (toastProxy.state === DISMISSING) {
|
||||
return toastProxy.element.dismiss();
|
||||
}
|
||||
}
|
||||
// either we're not in the dismissing state
|
||||
// or we're calling this before the element is created
|
||||
// so just return a resolved promise
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
export function loadOverlay(opts: ToastOptions): Promise<HTMLIonToastElement> {
|
||||
const element = ensureElementInBody('ion-toast-controller') as HTMLIonToastControllerElement;
|
||||
return hydrateElement(element).then(() => {
|
||||
return element.create(opts);
|
||||
});
|
||||
}
|
||||
|
||||
export interface ToastProxy {
|
||||
present(): Promise<void>;
|
||||
dismiss(): Promise<void>;
|
||||
onDidDismiss(callback: (data: any, role: string) => void): void;
|
||||
onWillDismiss(callback: (data: any, role: string) => void): void;
|
||||
}
|
||||
|
||||
export interface ToastProxyInternal extends ToastProxy {
|
||||
id: number;
|
||||
opts: ToastOptions;
|
||||
state: number;
|
||||
element: HTMLIonToastElement;
|
||||
onDidDismissHandler?: (data: any, role: string) => void;
|
||||
onWillDismissHandler?: (data: any, role: string) => void;
|
||||
}
|
||||
|
||||
export const PRESENTING = 1;
|
||||
export const DISMISSING = 2;
|
||||
|
||||
const ION_TOAST_DID_DISMISS_EVENT = 'ionToastDidDismiss';
|
||||
const ION_TOAST_WILL_DISMISS_EVENT = 'ionToastWillDismiss';
|
||||
32
packages/angular/src/util/util.ts
Normal file
32
packages/angular/src/util/util.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
export function hydrateElement(element: any) {
|
||||
return element.componentOnReady();
|
||||
}
|
||||
|
||||
export function getElement(elementName: string) {
|
||||
return document.querySelector(elementName);
|
||||
}
|
||||
|
||||
export function ensureElementInBody(elementName: string) {
|
||||
let element = getElement(elementName);
|
||||
if (!element) {
|
||||
element = document.createElement(elementName);
|
||||
document.body.appendChild(element);
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
export function removeAllNodeChildren(element: HTMLElement) {
|
||||
while (element.firstChild) {
|
||||
element.removeChild(element.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
export function isString(something: any) {
|
||||
return typeof something === 'string' ? true : false;
|
||||
}
|
||||
|
||||
export function getIonApp(): Promise<HTMLIonAppElement> {
|
||||
const element = ensureElementInBody('ion-app') as HTMLIonAppElement;
|
||||
return element.componentOnReady();
|
||||
}
|
||||
73
packages/core/CHANGELOG.md
Normal file
73
packages/core/CHANGELOG.md
Normal file
@@ -0,0 +1,73 @@
|
||||
<a name="0.1.4-1"></a>
|
||||
## [0.1.4-1](https://github.com/ionic-team/ionic/compare/v0.1.4-0...v0.1.4-1) (2018-03-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ion-router:** fixes routing algorithm ([c8a27b7](https://github.com/ionic-team/ionic/commit/c8a27b7))
|
||||
* **overlays:** bundling of overlays ([9650bec](https://github.com/ionic-team/ionic/commit/9650bec))
|
||||
* **routing:** flickering (part 1) ([7b264f9](https://github.com/ionic-team/ionic/commit/7b264f9))
|
||||
* **tabs:** do not select when using router ([174d9b5](https://github.com/ionic-team/ionic/commit/174d9b5))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **virtual-scroll:** adds JSX support ([dc8b363](https://github.com/ionic-team/ionic/commit/dc8b363))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.4-0"></a>
|
||||
## [0.1.4-0](https://github.com/ionic-team/ionic/compare/v0.1.3...v0.1.4-0) (2018-03-06)
|
||||
|
||||
### Refactor
|
||||
|
||||
- Refactored navigation system
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **testing:** do not throw error for missing Ionic global ([aa91d11](https://github.com/ionic-team/ionic/commit/aa91d11))
|
||||
* **zone:** forgot to remove console.logs ([4ec3e48](https://github.com/ionic-team/ionic/commit/4ec3e48))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.3"></a>
|
||||
## [0.1.3](https://github.com/ionic-team/ionic/compare/v0.1.2...v0.1.3) (2018-03-03)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* Updates to latest stencil, that includes the zone bypassing abilities.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ts:** ts strict fixes ([8ff02c7](https://github.com/ionic-team/ionic/commit/8ff02c7))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.2"></a>
|
||||
## [0.1.2](https://github.com/ionic-team/ionic/compare/v0.1.1...v0.1.2) (2018-03-03)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **events:** bypass ngzone ([7366c38](https://github.com/ionic-team/ionic/commit/7366c38))
|
||||
* **scroll:** watchdog + simplification ([33a6274](https://github.com/ionic-team/ionic/commit/33a6274))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **scroll:** clearInterval just to be safe ([6da9882](https://github.com/ionic-team/ionic/commit/6da9882))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.1"></a>
|
||||
## [0.1.1](https://github.com/ionic-team/ionic/commit/291e85e61128b2f3101d9cea6b42d4cf751dc481) (2018-03-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **button:** pass the property type instead of hardcoding button ([10e481a](https://github.com/ionic-team/ionic/commit/10e481a))
|
||||
|
||||
|
||||
|
||||
<a name="0.1.0"></a>
|
||||
## [0.1.0](https://github.com/ionic-team/ionic/commit/43a8c4c7a719169336a84964fc1c737562d764a6) (2018-03-01)
|
||||
@@ -20,7 +20,7 @@ The Ionic Core package contains the Web Components that make up the reusable UI
|
||||
|
||||
Easiest way to start using Ionic Core is by adding a script tag to the CDN:
|
||||
|
||||
<script src="https://unpkg.com/@ionic/core@0.1.4-6/dist/ionic.js"></script>
|
||||
<script src="https://unpkg.com/@ionic/core@0.1.4-1/dist/ionic.js"></script>
|
||||
|
||||
Any Ionic component added to the webpage will automatically load. This includes writing the component tag directly in HTML, or using JavaScript such as `document.createElement('ion-toggle')`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "0.1.4-6",
|
||||
"version": "0.1.4-1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -41,8 +41,8 @@
|
||||
"@ionic/discover": "0.3.3",
|
||||
"chokidar": "1.7.0",
|
||||
"ecstatic": "2.2.1",
|
||||
"opn": "5.3.0",
|
||||
"tiny-lr": "1.1.1"
|
||||
"opn": "5.2.0",
|
||||
"tiny-lr": "1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"anymatch": {
|
||||
@@ -5601,9 +5601,9 @@
|
||||
}
|
||||
},
|
||||
"http-parser-js": {
|
||||
"version": "0.4.11",
|
||||
"resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.11.tgz",
|
||||
"integrity": "sha512-QCR5O2AjjMW8Mo4HyI1ctFcv+O99j/0g367V3YoVnrNw5hkDvAWZD0lWGcc+F4yN3V55USPCVix4efb75HxFfA==",
|
||||
"version": "0.4.10",
|
||||
"resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz",
|
||||
"integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=",
|
||||
"dev": true
|
||||
},
|
||||
"http-signature": {
|
||||
@@ -9678,9 +9678,9 @@
|
||||
}
|
||||
},
|
||||
"opn": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz",
|
||||
"integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/opn/-/opn-5.2.0.tgz",
|
||||
"integrity": "sha512-Jd/GpzPyHF4P2/aNOVmS3lfMSWV9J7cOhCG1s08XCEAsPkB7lp6ddiU0J7XzyQRDUh8BqJ7PchfINjR8jyofRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-wsl": "1.1.0"
|
||||
@@ -12024,28 +12024,17 @@
|
||||
"dev": true
|
||||
},
|
||||
"tiny-lr": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz",
|
||||
"integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.0.tgz",
|
||||
"integrity": "sha512-f4X68a6KHcCx/XJcZUKAa92APjY9EHxuGOzRFmPRjf+fOp1E7fi4dGJaHMxvRBxwZrHrIvn/AwkFaDS7O1WZDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"body": "5.1.0",
|
||||
"debug": "3.1.0",
|
||||
"debug": "2.6.9",
|
||||
"faye-websocket": "0.10.0",
|
||||
"livereload-js": "2.3.0",
|
||||
"object-assign": "4.1.1",
|
||||
"qs": "6.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tmp": {
|
||||
@@ -12680,7 +12669,7 @@
|
||||
"integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"http-parser-js": "0.4.11",
|
||||
"http-parser-js": "0.4.10",
|
||||
"websocket-extensions": "0.1.3"
|
||||
}
|
||||
},
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "0.1.4-6",
|
||||
"version": "0.1.4-1",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -1,6 +1,6 @@
|
||||
# End-To-End Testing Scripts
|
||||
|
||||
This document describes the process of installing the dependencies for, running, and writing end-to-end tests for ionic core. Your working directory is assumed to be `core`.
|
||||
This document describes the process of installing the dependencies for, running, and writing end-to-end tests for ionic core. Your working directory is assumed to be `packages/core`.
|
||||
|
||||
---
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
|
||||
### 2. Run `npm install`
|
||||
|
||||
cd core
|
||||
cd packages/core
|
||||
npm install
|
||||
|
||||
|
||||
Notice that `@ionic/core` lives in `core`.
|
||||
Notice that `@ionic/core` lives in `packages/core`.
|
||||
|
||||
### 3. Run `npm run dev`
|
||||
|
||||
Make sure you are inside the `core` directory.
|
||||
Make sure you are inside the `packages/core` directory.
|
||||
|
||||
npm run dev
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
CD to core package:
|
||||
|
||||
cd core
|
||||
cd packages/core
|
||||
|
||||
|
||||
Pull latest:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user