mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(events): create events module and remove ion-events component
This commit is contained in:
472
packages/angular/package-lock.json
generated
472
packages/angular/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,51 +0,0 @@
|
||||
import { Component, Listen, Method } from '@stencil/core';
|
||||
|
||||
const subscriptions = new Map<string, ((event?: any) => void)[]>();
|
||||
|
||||
@Component({
|
||||
tag: 'ion-events'
|
||||
})
|
||||
export class Events {
|
||||
|
||||
@Method()
|
||||
subscribe(topic: string, handler: (event?: any) => void) {
|
||||
const handlers = subscriptions.get(topic) || [];
|
||||
handlers.push(handler);
|
||||
subscriptions.set(topic, handlers);
|
||||
}
|
||||
|
||||
@Method()
|
||||
unsubscribe(topic: string, handler: (event?: any) => void) {
|
||||
const handlers = subscriptions.get(topic) || [];
|
||||
const newHandlers = handlers.filter(fun => fun !== handler);
|
||||
subscriptions.set(topic, newHandlers);
|
||||
}
|
||||
|
||||
@Method()
|
||||
publish(topic: string, event?: any) {
|
||||
return publishImpl(topic, event);
|
||||
}
|
||||
|
||||
@Listen('window:online')
|
||||
@Listen('window:offline')
|
||||
@Listen('window:orientationchange')
|
||||
online(event: Event) {
|
||||
return publishImpl(`app:${event.type}`, event);
|
||||
}
|
||||
|
||||
@Listen('window:statusTap')
|
||||
statusTap(event: Event) {
|
||||
return publishImpl(`app:${event.type}`, event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// make this method async just to give the browser a chance to chill and do what it needs to do before firing this off
|
||||
function publishImpl(topic: string, event: any): Promise<any> {
|
||||
return Promise.resolve().then(() => {
|
||||
const handlers = subscriptions.get(topic) || [];
|
||||
for (const handler of handlers) {
|
||||
handler(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
# ion-events
|
||||
|
||||
|
||||
|
||||
<!-- Auto Generated Below -->
|
||||
|
||||
|
||||
## Methods
|
||||
|
||||
#### publish()
|
||||
|
||||
|
||||
#### subscribe()
|
||||
|
||||
|
||||
#### unsubscribe()
|
||||
|
||||
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
*Built with [StencilJS](https://stenciljs.com/)*
|
||||
@@ -1,54 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { By, until } = require('selenium-webdriver');
|
||||
const { register, Page, platforms } = require('../../../../../scripts/e2e');
|
||||
const expect = require('chai').expect;
|
||||
|
||||
class E2ETestPage extends Page {
|
||||
constructor(driver, platform) {
|
||||
super(driver, `http://localhost:3333/src/components/events/test/basic?ionicplatform=${platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
platforms.forEach(platform => {
|
||||
describe('events/basic', () => {
|
||||
|
||||
register('should init', driver => {
|
||||
const page = new E2ETestPage(driver, platform);
|
||||
return page.navigate('#content');
|
||||
});
|
||||
|
||||
register('subscribers should receive event on or shortly after button click', async (driver, testContext) => {
|
||||
testContext.timeout(2000);
|
||||
const page = new E2ETestPage(driver, platform);
|
||||
await wait(300);
|
||||
|
||||
// go to page two
|
||||
const publishButtonSelector = 'ion-button';
|
||||
const publishButton = await getElement(driver, publishButtonSelector);
|
||||
publishButton.click();
|
||||
await wait(300);
|
||||
|
||||
const secretOneElement = await getElement(driver, '.secret-one');
|
||||
const secretOneText = await secretOneElement.getText();
|
||||
expect(secretOneText).to.equal('Taco');
|
||||
|
||||
const secretTwoElement = await getElement(driver, '.secret-two');
|
||||
const secretTwoText = await secretTwoElement.getText();
|
||||
expect(secretTwoText).to.equal('Burrito');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function getElement(driver, selector) {
|
||||
driver.wait(until.elementLocated(By.css(selector)));
|
||||
const element = driver.findElement(By.css(selector));
|
||||
await driver.wait(until.elementIsVisible(driver.findElement(By.css(selector))));
|
||||
return element;
|
||||
}
|
||||
|
||||
function wait(duration) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, duration);
|
||||
})
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Events</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<script src="/dist/ionic.js"></script>
|
||||
</head>
|
||||
|
||||
<body onload="runTest()">
|
||||
<ion-events></ion-events>
|
||||
<ion-app>
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Events</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content" padding>
|
||||
|
||||
<div class="container">
|
||||
<div>
|
||||
The first secret is: <span class="secret-one">TBD</span>
|
||||
</div>
|
||||
<div>
|
||||
The second secret is: <span class="secret-two">TBD</span>
|
||||
</div>
|
||||
</div>
|
||||
<ion-button class="publish">Publish the data</ion-button>
|
||||
</ion-content>
|
||||
|
||||
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
|
||||
async function runTest() {
|
||||
const ionEvents = document.querySelector('ion-events');
|
||||
await ionEvents.componentOnReady();
|
||||
|
||||
ionEvents.subscribe('secretOne', (secretOne) => {
|
||||
const span = document.querySelector('.secret-one');
|
||||
span.textContent = secretOne;
|
||||
});
|
||||
|
||||
ionEvents.subscribe('secretTwo', (secretTwo) => {
|
||||
const span = document.querySelector('.secret-two');
|
||||
span.textContent = secretTwo;
|
||||
});
|
||||
|
||||
const publishButton = document.querySelector('ion-button');
|
||||
publishButton.addEventListener('click', async () => {
|
||||
ionEvents.publish('secretOne', 'Taco');
|
||||
ionEvents.publish('secretTwo', 'Burrito');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
101
packages/core/src/global/events.ts
Normal file
101
packages/core/src/global/events.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
|
||||
export class Events {
|
||||
private c: {[topic: string]: Function[]} = [] as any;
|
||||
|
||||
/**
|
||||
* Subscribe to an event topic. Events that get posted to that topic will trigger the provided handler.
|
||||
*
|
||||
* @param {string} topic the topic to subscribe to
|
||||
* @param {function} handler the event handler
|
||||
*/
|
||||
subscribe(topic: string, ...handlers: Function[]) {
|
||||
if (!this.c[topic]) {
|
||||
this.c[topic] = [];
|
||||
}
|
||||
handlers.forEach((handler) => {
|
||||
this.c[topic].push(handler);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from the given topic. Your handler will no longer receive events published to this topic.
|
||||
*
|
||||
* @param {string} topic the topic to unsubscribe from
|
||||
* @param {function} handler the event handler
|
||||
*
|
||||
* @return true if a handler was removed
|
||||
*/
|
||||
unsubscribe(topic: string, handler: Function = null) {
|
||||
let t = this.c[topic];
|
||||
if (!t) {
|
||||
// Wasn't found, wasn't removed
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!handler) {
|
||||
// Remove all handlers for this topic
|
||||
delete this.c[topic];
|
||||
return true;
|
||||
}
|
||||
|
||||
// We need to find and remove a specific handler
|
||||
let i = t.indexOf(handler);
|
||||
|
||||
if (i < 0) {
|
||||
// Wasn't found, wasn't removed
|
||||
return false;
|
||||
}
|
||||
|
||||
t.splice(i, 1);
|
||||
|
||||
// If the channel is empty now, remove it from the channel map
|
||||
if (!t.length) {
|
||||
delete this.c[topic];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish an event to the given topic.
|
||||
*
|
||||
* @param {string} topic the topic to publish to
|
||||
* @param {any} eventData the data to send as the event
|
||||
*/
|
||||
publish(topic: string, ...args: any[]) {
|
||||
var t = this.c[topic];
|
||||
if (!t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let responses: any[] = [];
|
||||
t.forEach((handler: any) => {
|
||||
responses.push(handler(...args));
|
||||
});
|
||||
return responses;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function setupEvents(win: Window, doc: Document) {
|
||||
const events = new Events();
|
||||
|
||||
win.addEventListener('online', ev => events.publish('app:online', ev));
|
||||
|
||||
win.addEventListener('offline', ev => events.publish('app:offline', ev));
|
||||
|
||||
win.addEventListener('orientationchange', ev => events.publish('app:rotated', ev));
|
||||
|
||||
win.addEventListener('statusTap', () => {
|
||||
const centerElm = doc.elementFromPoint(win.innerWidth / 2, win.innerHeight / 2);
|
||||
if (centerElm) {
|
||||
const scrollElm = centerElm.closest('ion-scroll');
|
||||
if (scrollElm) {
|
||||
scrollElm.componentOnReady(() => scrollElm.scrollToTop(300));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return events;
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import 'ionicons';
|
||||
import { createConfigController } from './config-controller';
|
||||
import { PLATFORM_CONFIGS, detectPlatforms, readQueryParam } from './platform-configs';
|
||||
import { createDomControllerClient } from './dom-controller';
|
||||
import { PLATFORM_CONFIGS, detectPlatforms, readQueryParam } from './platform-configs';
|
||||
import { setupEvents } from './events';
|
||||
|
||||
|
||||
const Ionic = (window as any).Ionic = (window as any).Ionic || {};
|
||||
@@ -30,6 +31,7 @@ Ionic.config = Context.config = createConfigController(
|
||||
Context.platforms
|
||||
);
|
||||
|
||||
Ionic.Events = Context.Events = setupEvents(window, document);
|
||||
|
||||
// first see if the mode was set as an attribute on <html>
|
||||
// which could have been set by the user, or by prerendering
|
||||
|
||||
187
packages/core/src/index.d.ts
vendored
187
packages/core/src/index.d.ts
vendored
@@ -1,187 +0,0 @@
|
||||
// Components
|
||||
export * from './components/action-sheet/action-sheet';
|
||||
|
||||
export { ActionSheetController } from './components/action-sheet-controller/action-sheet-controller';
|
||||
export * from './components/alert/alert';
|
||||
export { AlertController } from './components/alert-controller/alert-controller';
|
||||
export {
|
||||
Animation,
|
||||
AnimationBuilder,
|
||||
AnimationController,
|
||||
AnimationOptions
|
||||
} from './components/animation-controller/animation-interface';
|
||||
export { App } from './components/app/app';
|
||||
export { Avatar } from './components/avatar/avatar';
|
||||
export { BackButton } from './components/back-button/back-button';
|
||||
export { Backdrop } from './components/backdrop/backdrop';
|
||||
export { Badge } from './components/badge/badge';
|
||||
export { Button } from './components/button/button';
|
||||
export { Buttons } from './components/buttons/buttons';
|
||||
export { Card } from './components/card/card';
|
||||
export { CardContent } from './components/card-content/card-content';
|
||||
export { CardHeader } from './components/card-header/card-header';
|
||||
export { CardTitle } from './components/card-title/card-title';
|
||||
export { Checkbox } from './components/checkbox/checkbox';
|
||||
export { Chip } from './components/chip/chip';
|
||||
export { ChipButton } from './components/chip-button/chip-button';
|
||||
export { Col } from './components/col/col';
|
||||
export { Content } from './components/content/content';
|
||||
export { Datetime } from './components/datetime/datetime';
|
||||
export { Fab } from './components/fab/fab';
|
||||
export { FabList } from './components/fab-list/fab-list';
|
||||
export { FabButton } from './components/fab-button/fab-button';
|
||||
export { Footer } from './components/footer/footer';
|
||||
export { Gesture, GestureCallback, GestureDetail } from './components/gesture/gesture';
|
||||
export { PanRecognizer } from './components/gesture/recognizers';
|
||||
export * from './components/gesture-controller/gesture-controller-utils';
|
||||
export { Grid } from './components/grid/grid';
|
||||
export { Header } from './components/header/header';
|
||||
export { InfiniteScroll } from './components/infinite-scroll/infinite-scroll';
|
||||
export { InfiniteScrollContent } from './components/infinite-scroll-content/infinite-scroll-content';
|
||||
export { Input } from './components/input/input';
|
||||
export { Item } from './components/item/item';
|
||||
export { ItemDivider } from './components/item-divider/item-divider';
|
||||
export { ItemOption } from './components/item-option/item-option';
|
||||
export { ItemSliding } from './components/item-sliding/item-sliding';
|
||||
export { KeyboardController } from './components/keyboard-controller/keyboard-controller';
|
||||
export * from './components/keyboard-controller/keys';
|
||||
export { Label } from './components/label/label';
|
||||
export { List } from './components/list/list';
|
||||
export { ListHeader } from './components/list-header/list-header';
|
||||
export * from './components/loading/loading';
|
||||
export { LoadingController } from './components/loading-controller/loading-controller';
|
||||
export { Menu } from './components/menu/menu';
|
||||
export {
|
||||
MenuController,
|
||||
MenuOverlayAnimation,
|
||||
MenuPushAnimation,
|
||||
MenuRevealAnimation
|
||||
} from './components/menu-controller/menu-controller';
|
||||
export * from './components/modal/modal';
|
||||
export { ModalController } from './components/modal-controller/modal-controller';
|
||||
export * from './components/nav/nav';
|
||||
export * from './components/nav/nav-interfaces';
|
||||
export { ViewController } from './components/nav/view-controller';
|
||||
export { Note } from './components/note/note';
|
||||
export { PickerColumnCmp } from './components/picker-column/picker-column';
|
||||
export * from './components/picker/picker';
|
||||
export { PickerController } from './components/picker-controller/picker-controller';
|
||||
export * from './components/popover/popover';
|
||||
export { PopoverController } from './components/popover-controller/popover-controller';
|
||||
export { RadioGroup } from './components/radio-group/radio-group';
|
||||
export { Radio, HTMLIonRadioElementEvent } from './components/radio/radio';
|
||||
export { Range, RangeEvent } from './components/range/range';
|
||||
export { RangeKnob } from './components/range-knob/range-knob';
|
||||
export { ReorderGroup } from './components/reorder-group/reorder-group';
|
||||
export {
|
||||
RouterEntry,
|
||||
RouterEntries,
|
||||
NavOutlet
|
||||
} from './components/router/router-utils';
|
||||
export { Row } from './components/row/row';
|
||||
export { Reorder } from './components/reorder/reorder';
|
||||
export { Scroll, ScrollCallback, ScrollDetail } from './components/scroll/scroll';
|
||||
export { Searchbar } from './components/searchbar/searchbar';
|
||||
export { Segment } from './components/segment/segment';
|
||||
export { SegmentButton } from './components/segment-button/segment-button';
|
||||
export { SelectPopoverOption, SelectPopover } from './components/select-popover/select-popover';
|
||||
export { Select } from './components/select/select';
|
||||
export { SelectOption } from './components/select-option/select-option';
|
||||
export { SkeletonText } from './components/skeleton-text/skeleton-text';
|
||||
export { Slide } from './components/slide/slide';
|
||||
export { Slides } from './components/slides/slides';
|
||||
export * from './components/spinner/spinner-configs';
|
||||
export { Spinner } from './components/spinner/spinner';
|
||||
export { SplitPane } from './components/split-pane/split-pane';
|
||||
export { Tab } from './components/tab/tab';
|
||||
export { TabButton } from './components/tab-button/tab-button';
|
||||
export { Tabs } from './components/tabs/tabs';
|
||||
export { Thumbnail } from './components/thumbnail/thumbnail';
|
||||
export { ToolbarTitle } from './components/title/title';
|
||||
export * from './components/toast/toast';
|
||||
export { ToastController } from './components/toast-controller/toast-controller';
|
||||
export { Toggle } from './components/toggle/toggle';
|
||||
export { Toolbar } from './components/toolbar/toolbar';
|
||||
|
||||
export { PlatformConfig } from './global/platform-configs';
|
||||
|
||||
// export all of the component declarations that are dynamically created
|
||||
export * from './components';
|
||||
|
||||
export { DomController, RafCallback } from './global/dom-controller';
|
||||
|
||||
export interface Config {
|
||||
get: (key: string, fallback?: any) => any;
|
||||
getBoolean: (key: string, fallback?: boolean) => boolean;
|
||||
getNumber: (key: string, fallback?: number) => number;
|
||||
}
|
||||
|
||||
export type CssClassMap = { [className: string]: boolean };
|
||||
|
||||
export interface BaseInputComponent {
|
||||
disabled: boolean;
|
||||
hasFocus: boolean;
|
||||
value: string;
|
||||
|
||||
fireFocus: () => void;
|
||||
fireBlur: () => void;
|
||||
}
|
||||
|
||||
export interface OverlayDismissEvent extends CustomEvent {
|
||||
detail: OverlayDismissEventDetail;
|
||||
}
|
||||
|
||||
export interface OverlayDismissEventDetail {
|
||||
data?: any;
|
||||
role?: string;
|
||||
}
|
||||
|
||||
export interface FrameworkDelegate {
|
||||
attachViewToDom(elementOrContainerToMountTo: any, elementOrComponentToMount: any, propsOrDataObj?: any, classesToAdd?: string[], escapeHatch?: any): Promise<FrameworkMountingData>;
|
||||
removeViewFromDom(elementOrContainerToUnmountFrom: any, elementOrComponentToUnmount: any, escapeHatch?: any): Promise<void>;
|
||||
}
|
||||
|
||||
export interface RouterDelegate {
|
||||
pushUrlState(urlSegment: string, stateObject?: any, title?: string): Promise<any>;
|
||||
popUrlState(): Promise<any>;
|
||||
}
|
||||
|
||||
export interface FrameworkMountingData {
|
||||
element: HTMLElement;
|
||||
component: any;
|
||||
data: any;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
namespace JSXElements {
|
||||
|
||||
export interface DOMAttributes {
|
||||
// for ion-menu and ion-split-pane
|
||||
main?: boolean;
|
||||
|
||||
padding?: boolean;
|
||||
['padding-top']?: boolean;
|
||||
['padding-bottom']?: boolean;
|
||||
['padding-left']?: boolean;
|
||||
['padding-right']?: boolean;
|
||||
['padding-horizontal']?: boolean;
|
||||
['padding-vertical']?: boolean;
|
||||
|
||||
margin?: boolean;
|
||||
['margin-top']?: boolean;
|
||||
['margin-bottom']?: boolean;
|
||||
['margin-left']?: boolean;
|
||||
['margin-right']?: boolean;
|
||||
['margin-horizontal']?: boolean;
|
||||
['margin-vertical']?: boolean;
|
||||
|
||||
['no-padding']?: boolean;
|
||||
['no-margin']?: boolean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface OverlayController {
|
||||
getTop(): HTMLElement;
|
||||
}
|
||||
Reference in New Issue
Block a user