chore() react beta 6 release (#18588)

* fix(react) attribute data for web components fix

* fix(react) attribute data for web components fix

* wip

* putting back cause issues

* update version 0.6-6

* wip

* fix(react) - fixing flash between moving between tabs

* update version to 0.6-7

* update to core 4.6

* update to 6-9 and tab button selected fix

* wrapping react router

* beta 6 release
This commit is contained in:
Ely Lucas
2019-07-02 10:08:23 -06:00
committed by GitHub
parent 45b82dc466
commit c6bb2730a8
22 changed files with 462 additions and 182 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/react",
"version": "0.0.6-3",
"version": "0.0.6",
"description": "React specific wrapper for @ionic/core",
"keywords": [
"ionic",
@ -36,14 +36,15 @@
"dist/"
],
"dependencies": {
"@ionic/core": "^4.6.0-dev.201906192117.6727cfc",
"@ionic/core": "^4.6.0",
"tslib": "^1.10.0"
},
"peerDependencies": {
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1"
"react-router-dom": "^5.0.1",
"ionicons": "^4.5.10-2"
},
"devDependencies": {
"@trust/webcrypto": "^0.9.2",
@ -53,6 +54,7 @@
"@types/react-dom": "^16.8.4",
"@types/react-router": "^4.4.4",
"@types/react-router-dom": "^4.3.1",
"ionicons": "^4.6.0",
"jest": "^24.8.0",
"jest-dom": "^3.4.0",
"np": "^5.0.1",

View File

@ -0,0 +1,37 @@
import React from 'react';
import { IonAppInner } from './proxies';
import { IonicConfig } from '@ionic/core';
import { IonicContext, IonicContextState } from './utils/IonicContext';
import { Platform } from './utils/platform';
import { getConfig, setConfig } from './utils/config';
interface IonAppProps {
initialConfig?: IonicConfig;
}
interface IonAppState extends IonicContextState { };
export class IonApp extends React.Component<IonAppProps, IonAppState> {
constructor(props: IonAppProps) {
super(props);
const ionicPlatform = new Platform();
this.state = {
getConfig: getConfig,
setConfig: setConfig,
platform: ionicPlatform
}
}
render() {
return (
<IonicContext.Provider value={this.state}>
<IonAppInner>{this.props.children}</IonAppInner>
</IonicContext.Provider>
);
}
};
export default IonAppProps;

View File

@ -2,7 +2,7 @@ import React from 'react';
import ReactDom from 'react-dom';
import { dashToPascalCase, attachEventProps } from './utils';
export function createReactComponent<PropType, ElementType>(tagName: string) {
export function createReactComponent<PropType, ElementType>(tagName: string, attributeValues: string[] = []) {
const displayName = dashToPascalCase(tagName);
type IonicReactInternalProps = {
@ -17,11 +17,9 @@ export function createReactComponent<PropType, ElementType>(tagName: string) {
}
class ReactComponent extends React.Component<InternalProps> {
componentRef: React.RefObject<ElementType>;
constructor(props: PropType & IonicReactInternalProps) {
super(props);
this.componentRef = React.createRef();
}
static get displayName() {
@ -40,10 +38,17 @@ export function createReactComponent<PropType, ElementType>(tagName: string) {
render() {
const { children, forwardedRef, ...cProps } = this.props;
const propsWithoutAttributeValues = Object.keys(cProps).reduce((oldValue, key) => {
if(attributeValues.indexOf(key) === -1) {
(oldValue as any)[key] = (cProps as any)[key];
}
return oldValue;
}, {});
return React.createElement(
tagName,
{
...cProps,
...propsWithoutAttributeValues,
ref: forwardedRef
},
children

View File

@ -63,4 +63,3 @@ export function createControllerComponent<OptionsType extends object, LoadingEle
}
}
}

View File

@ -1,6 +1,7 @@
import { defineCustomElements } from '@ionic/core/loader';
export { AlertButton, AlertInput } from '@ionic/core';
export { IonApp } from './IonApp'
export * from './proxies';
// createControllerComponent
@ -17,8 +18,38 @@ export { IonPopover } from './IonPopover';
export { IonPage } from './IonPage';
export { IonTabs } from './navigation/IonTabs';
export { IonTabBar } from './navigation/IonTabBar';
export { IonRouterOutlet } from './navigation/IonRouterOutlet';
export { IonBackButton } from './navigation/IonBackButton';
export { IonRouterWrapped as IonRouter } from './navigation/IonRouter';
// Routing
export { IonRouterOutlet } from './navigation/routing/IonRouterOutlet';
export { IonReactRouter } from './navigation/routing/ReactRouter/IonReactRouter';
// Ionic Context
export { IonicContext } from './utils/IonicContext';
export { useIonicConfig } from './utils/utilHooks';
// Icons
import { addIcons } from 'ionicons';
import { close, reorder, menu, arrowDown, arrowForward, arrowBack, search, closeCircle } from 'ionicons/icons';
defineCustomElements(window);
// Icons that are used by internal components
addIcons({
'ios-close': close.ios,
'md-close': close.md,
'ios-reorder': reorder.ios,
'md-reorder': reorder.md,
'ios-menu': menu.ios,
'md-menu': menu.md,
'ios-arrow-forward': arrowForward.ios,
'md-arrow-forward': arrowForward.md,
'ios-arrow-back': arrowBack.ios,
'md-arrow-back': arrowBack.md,
'ios-arrow-down': arrowDown.ios,
'md-arrow-down': arrowDown.md,
'ios-search': search.ios,
'md-search': search.md,
'ios-close-circle': closeCircle.ios,
'md-close-circle': closeCircle.md,
});

View File

@ -1,6 +1,6 @@
import { JSX as LocalJSX } from '@ionic/core';
import React from 'react';
import { NavContext } from './NavContext';
import { NavContext } from './routing/NavContext';
import { IonBackButtonInner } from '../proxies';
type BackButtonProps = LocalJSX.IonBackButton & {

View File

@ -1,129 +0,0 @@
import React from 'react';
import { withRouter, RouteComponentProps, matchPath, RouteProps, match, Switch } from 'react-router';
import { generateUniqueId } from '../utils';
import { IonRouterOutletInner } from '../proxies';
import { View } from '../View';
import { NavContext } from './NavContext';
import { ViewItemManager } from './ViewItemManager';
import { ViewItem } from './ViewItem';
type ChildProps = RouteProps & {
computedMatch: match<any>
}
type IonRouterOutletProps = RouteComponentProps & {
id?: string;
children?: React.ReactElement<ChildProps>[] | React.ReactElement<ChildProps>;
};
type IonRouterOutletState = {}
class IonRouterOutletUnWrapped extends React.Component<IonRouterOutletProps, IonRouterOutletState> {
containerEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef();
context!: React.ContextType<typeof NavContext>;
id: string;
constructor(props: IonRouterOutletProps) {
super(props);
this.id = this.props.id || generateUniqueId();
}
componentDidMount() {
const views: ViewItem[] = [];
let activeId: string;
React.Children.forEach(this.props.children, (child: React.ReactElement<ChildProps>) => {
if (child.type === Switch) {
/**
* If the first child is a Switch, loop through its children to build the viewStack
*/
React.Children.forEach(child.props.children, (grandChild: React.ReactElement<ChildProps>) => {
addView.call(this, grandChild);
});
} else {
addView.call(this, child);
}
});
this.context.registerViewStack(this.id, activeId, views, this.containerEl.current, this.props.location);
function addView(child: React.ReactElement<any>) {
const location = this.props.history.location;
const viewId = generateUniqueId();
const key = generateUniqueId();
const element = child;
const match: ViewItem['match'] = matchPath(location.pathname, child.props);
const view: ViewItem = {
id: viewId,
key,
match,
element,
mount: true,
show: !!match,
ref: React.createRef(),
childProps: child.props
};
if (!!match) {
activeId = viewId;
};
views.push(view);
return activeId;
}
}
componentWillUnmount() {
this.context.removeViewStack(this.id);
}
renderChild(item: ViewItem) {
const component = React.cloneElement(item.element, {
computedMatch: item.match
});
return component;
}
render() {
return (
<NavContext.Consumer>
{context => {
this.context = context;
const viewStack = context.viewStacks[this.id];
const activeId = viewStack ? viewStack.activeId : '';
const views = (viewStack || { views: [] }).views.filter(x => x.show);
return (
<IonRouterOutletInner data-id={this.id} ref={this.containerEl}>
{views.map((item) => {
let props: any = {};
if (item.id === activeId) {
props = {
'className': ' ion-page-invisible'
};
} else {
props = {
'aria-hidden': true,
'className': 'ion-page-hidden'
};
}
return (
<ViewItemManager
id={item.id}
key={item.key}
mount={item.mount}
>
<View
ref={item.ref}
{...props}
>
{this.renderChild(item)}
</View>
</ViewItemManager>
);
})}
</IonRouterOutletInner>
);
}}
</NavContext.Consumer>
);
}
}
export const IonRouterOutlet = /*@__PURE__*/withRouter(IonRouterOutletUnWrapped);

View File

@ -1,6 +1,6 @@
import React from 'react';
import { IonTabBar } from './IonTabBar';
import { IonRouterOutlet } from './IonRouterOutlet';
import { IonRouterOutlet } from './routing/IonRouterOutlet';
type Props = {
children: React.ReactNode;
@ -25,7 +25,6 @@ const tabsInner: React.CSSProperties = {
contain: 'layout size style'
};
export class IonTabs extends React.Component<Props> {
render() {

View File

@ -1,7 +1,7 @@
import React from 'react';
import { IonLifeCycleContext } from '../../lifecycle';
import { DefaultIonLifeCycleContext } from '../../lifecycle/IonLifeCycleContext';
import { NavContext } from './NavContext';
import { NavContext } from './routing/NavContext';
interface StackItemManagerProps {
id: string;

View File

@ -0,0 +1,70 @@
import React from 'react';
import { generateUniqueId } from '../../utils/utils';
import { IonRouterOutletInner } from '../../proxies';
import { View } from '../../View';
import { NavContext } from './NavContext';
import { ViewItemManager } from '../ViewItemManager';
type IonRouterOutletProps = {
id?: string;
};
type IonRouterOutletState = {}
export class IonRouterOutlet extends React.Component<IonRouterOutletProps, IonRouterOutletState> {
containerEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef();
context!: React.ContextType<typeof NavContext>;
id: string;
constructor(props: IonRouterOutletProps) {
super(props);
this.id = this.props.id || generateUniqueId();
}
componentDidMount() {
this.context.setupIonRouter(this.id, this.props.children, this.containerEl.current);
}
componentWillUnmount() {
this.context.removeViewStack(this.id);
}
render() {
return (
<NavContext.Consumer>
{context => {
this.context = context;
const viewStack = context.viewStacks[this.id];
const activeId = viewStack ? viewStack.activeId : '';
const views = (viewStack || { views: [] }).views.filter(x => x.show);
return (
<IonRouterOutletInner data-id={this.id} ref={this.containerEl}>
{views.map((item) => {
let props: any = {};
if (item.id === activeId) {
props = {
'className': ' ion-page-invisible'
};
}
return (
<ViewItemManager
id={item.id}
key={item.key}
mount={item.mount}
>
<View
ref={item.ref}
{...props}
>
{this.context.renderChild(item)}
</View>
</ViewItemManager>
);
})}
</IonRouterOutletInner>
);
}}
</NavContext.Consumer>
);
}
}

View File

@ -1,13 +1,10 @@
import React from 'react';
import { ViewItem } from './ViewItem';
import React, { ReactNode } from 'react';
import { ViewItem } from './ReactRouter/ViewItem';
import { NavDirection } from '@ionic/core';
import { Location } from 'history';
export interface ViewStack {
routerOutlet: HTMLIonRouterOutletElement;
activeId?: string,
// prevId?: string,
views: ViewItem[]
}
@ -18,8 +15,9 @@ export interface ViewStacks {
export interface NavContextState {
hideView: (viewId: string) => void;
viewStacks: ViewStacks;
registerViewStack: (stack: string, activeId: string, stackItems: ViewItem[], ionRouterOutlet: HTMLIonRouterOutletElement, location: Location) => void;
setupIonRouter: (id: string, children: ReactNode, routerOutlet: HTMLIonRouterOutletElement) => void;
removeViewStack: (stack: string) => void;
renderChild: (item: ViewItem) => void;
goBack: (defaultHref?: string) => void;
transitionView: (enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOuter: HTMLIonRouterOutletElement, direction: NavDirection) => void;
}
@ -28,8 +26,9 @@ export const NavContext = /*@__PURE__*/React.createContext<NavContextState>({
viewStacks: {},
hideView: () => { navContextNotFoundError(); },
goBack: () => { navContextNotFoundError(); },
registerViewStack: () => { navContextNotFoundError(); },
setupIonRouter: () => { navContextNotFoundError() },
removeViewStack: () => { navContextNotFoundError(); },
renderChild: () => { navContextNotFoundError(); },
transitionView: () => { navContextNotFoundError(); }
});

View File

@ -1,26 +1,32 @@
import React from 'react';
import { withRouter, RouteComponentProps, matchPath, match, Redirect } from 'react-router-dom';
import React, { ReactNode } from 'react';
import { RouteComponentProps, matchPath, match, Redirect, Switch, RouteProps, BrowserRouterProps, BrowserRouter, withRouter } from 'react-router-dom';
import { UnregisterCallback, Action as HistoryAction, Location as HistoryLocation } from 'history';
import { NavContext, NavContextState, ViewStacks, ViewStack } from './NavContext';
import { NavContext, NavContextState, ViewStacks, ViewStack } from '../NavContext';
import { ViewItem } from './ViewItem';
import { NavDirection } from '@ionic/core';
import { generateUniqueId } from '../utils';
import { generateUniqueId } from '../../../utils';
interface IonRouterProps extends RouteComponentProps { }
interface IonRouterState extends NavContextState { }
interface IonReactRouterProps extends RouteComponentProps { }
interface IonReactRouterState extends NavContextState { }
class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
interface IonRouteData {
match: match<{ tab: string }>;
childProps: RouteProps;
}
class IonNavManager extends React.Component<IonReactRouterProps, IonReactRouterState> {
listenUnregisterCallback: UnregisterCallback;
activeViewId?: string;
prevViewId?: string;
constructor(props: IonRouterProps) {
constructor(props: IonReactRouterProps) {
super(props);
this.state = {
viewStacks: {},
hideView: this.hideView.bind(this),
registerViewStack: this.registerView.bind(this),
setupIonRouter: this.setupIonRouter.bind(this),
removeViewStack: this.removeViewStack.bind(this),
renderChild: this.renderChild.bind(this),
goBack: this.goBack.bind(this),
transitionView: this.transitionView.bind(this)
};
@ -47,14 +53,14 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
}
findViewInfoByLocation(location: HistoryLocation, viewStacks: ViewStacks) {
let view: ViewItem;
let match: match<{ tab: string }>;
let view: ViewItem<IonRouteData>;
let match: IonRouteData["match"];
let viewStack: ViewStack;
const keys = Object.keys(viewStacks);
keys.some(key => {
const vs = viewStacks[key];
return vs.views.some(x => {
match = matchPath(location.pathname, x.childProps)
match = matchPath(location.pathname, x.routeData.childProps)
if (match) {
view = x;
viewStack = vs;
@ -64,12 +70,12 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
});
})
const result: { view: ViewItem, viewStack: ViewStack, match: ViewItem['match'] } = { view, viewStack, match };
const result = { view, viewStack, match };
return result;
}
findViewInfoById(id: string, viewStacks: ViewStacks) {
let view: ViewItem;
let view: ViewItem<IonRouteData>;
let viewStack: ViewStack;
const keys = Object.keys(viewStacks);
keys.some(key => {
@ -96,7 +102,7 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
const { view: leavingView } = this.findViewInfoById(this.activeViewId, viewStacks);
if (leavingView && leavingView.match.url === location.pathname) {
if (leavingView && leavingView.routeData.match.url === location.pathname) {
return;
}
@ -111,13 +117,13 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
enteringView.show = true;
enteringView.mount = true;
enteringView.match = match;
enteringView.routeData.match = match;
enteringViewStack.activeId = enteringView.id;
this.activeViewId = enteringView.id;
if (leavingView) {
this.prevViewId = leavingView.id
if (leavingView.match.params.tab === enteringView.match.params.tab) {
if (leavingView.routeData.match.params.tab === enteringView.routeData.match.params.tab) {
if (action === 'PUSH') {
direction = direction || 'forward';
} else {
@ -155,7 +161,50 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
this.listenUnregisterCallback();
}
registerView(stack: string, activeId: string, stackItems: ViewItem[], routerOutlet: HTMLIonRouterOutletElement, location: HistoryLocation) {
setupIonRouter(id: string, children: ReactNode, routerOutlet: HTMLIonRouterOutletElement) {
const views: ViewItem[] = [];
let activeId: string;
React.Children.forEach(children, (child: React.ReactElement) => {
if (child.type === Switch) {
/**
* If the first child is a Switch, loop through its children to build the viewStack
*/
React.Children.forEach(child.props.children, (grandChild: React.ReactElement) => {
addView.call(this, grandChild);
});
} else {
addView.call(this, child);
}
});
this.registerViewStack(id, activeId, views, routerOutlet, this.props.location);
function addView(child: React.ReactElement<any>) {
const location = this.props.history.location;
const viewId = generateUniqueId();
const key = generateUniqueId();
const element = child;
const match: IonRouteData['match'] = matchPath(location.pathname, child.props);
const view: ViewItem<IonRouteData> = {
id: viewId,
key,
routeData: {
match,
childProps: child.props
},
element,
mount: true,
show: !!match,
ref: React.createRef()
};
if (!!match) {
activeId = viewId;
};
views.push(view);
return activeId;
}
}
registerViewStack(stack: string, activeId: string, stackItems: ViewItem[], routerOutlet: HTMLIonRouterOutletElement, location: HistoryLocation) {
this.setState((prevState) => {
const prevViewStacks = Object.assign({}, prevState.viewStacks);
prevViewStacks[stack] = {
@ -191,10 +240,17 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
});
}
renderChild(item: ViewItem<IonRouteData>) {
const component = React.cloneElement(item.element, {
computedMatch: item.routeData.match
});
return component;
}
findActiveView(views: ViewItem[]) {
let view: ViewItem | undefined;
let view: ViewItem<IonRouteData> | undefined;
views.some(x => {
const match = matchPath(this.props.location.pathname, x.childProps)
const match = matchPath(this.props.location.pathname, x.routeData.childProps)
if (match) {
view = x;
return true;
@ -209,7 +265,7 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
if (leavingView) {
const { view: enteringView } = this.findViewInfoById(leavingView.prevId, this.state.viewStacks);
if (enteringView) {
this.props.history.replace(enteringView.match.url, { direction: 'back' });
this.props.history.replace(enteringView.routeData.match.url, { direction: 'back' });
} else {
this.props.history.replace(defaultHref, { direction: 'back' });
}
@ -260,4 +316,16 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
}
};
export const IonRouterWrapped = withRouter(IonRouter);
const IonNavManagerWithRouter = withRouter(IonNavManager);
IonNavManagerWithRouter.displayName = 'IonNavManager';
export class IonReactRouter extends React.Component<BrowserRouterProps> {
render() {
const { children, ...props } = this.props;
return (
<BrowserRouter {...props}>
<IonNavManagerWithRouter>{children}</IonNavManagerWithRouter>
</BrowserRouter>
);
}
}

View File

@ -1,13 +1,10 @@
import { match, RouteProps } from 'react-router-dom';
export type ViewItem = {
export type ViewItem<RouteData = any> = {
id: string;
key: string;
match: match<{ tab: string }>;
element: React.ReactElement<any>;
ref?: React.RefObject<HTMLElement>;
routeData: RouteData;
prevId?: string;
mount: boolean;
show: boolean;
childProps?: RouteProps;
}

View File

@ -5,7 +5,7 @@ import { JSX as IoniconsJSX } from 'ionicons';
// ionicons
export const IonIcon = /*@__PURE__*/createReactComponent<IoniconsJSX.IonIcon & ReactProps, HTMLIonIconElement>('ion-icon');
export const IonIcon = /*@__PURE__*/createReactComponent<IoniconsJSX.IonIcon & ReactProps, HTMLIonIconElement>('ion-icon', ['icon']);
// /*@__PURE__*/createReactComponent
export const IonTabBarInner = /*@__PURE__*/createReactComponent<JSX.IonTabBar & ReactProps, HTMLIonTabBarElement>('ion-tab-bar');
@ -14,7 +14,7 @@ export const IonBackButtonInner = /*@__PURE__*/createReactComponent<JSX.IonBackB
export const IonTab = /*@__PURE__*/createReactComponent<JSX.IonTab & ReactProps, HTMLIonTabElement>('ion-tab');
export const IonTabButton = /*@__PURE__*/createReactComponent<JSX.IonTabButton & ReactProps, HTMLIonTabButtonElement>('ion-tab-button');
export const IonAnchor = /*@__PURE__*/createReactComponent<JSX.IonAnchor & ReactProps, HTMLIonAnchorElement>('ion-anchor');
export const IonApp = /*@__PURE__*/createReactComponent<JSX.IonApp & ReactProps, HTMLIonAppElement>('ion-app');
export const IonAppInner = /*@__PURE__*/createReactComponent<JSX.IonApp & ReactProps, HTMLIonAppElement>('ion-app');
export const IonAvatar = /*@__PURE__*/createReactComponent<JSX.IonAvatar & ReactProps, HTMLIonAvatarElement>('ion-avatar');
export const IonBackdrop = /*@__PURE__*/createReactComponent<JSX.IonBackdrop & ReactProps, HTMLIonBackdropElement>('ion-backdrop');
export const IonBadge = /*@__PURE__*/createReactComponent<JSX.IonBadge & ReactProps, HTMLIonBadgeElement>('ion-badge');

View File

@ -0,0 +1,15 @@
import React from 'react';
import { Platform } from './platform';
import { IonicConfig } from '@ionic/core';
export interface IonicContextState {
getConfig: () => IonicConfig | void;
setConfig: (config: IonicConfig) => void;
platform: Platform | undefined;
}
export const IonicContext = /*@__PURE__*/React.createContext<IonicContextState>({
getConfig: () => {},
setConfig: () => {},
platform: undefined
});

View File

@ -0,0 +1,25 @@
import { Config, IonicConfig } from '@ionic/core';
export function getConfig() {
const coreConfig = getCoreConfig();
const config: IonicConfig = Array.from((coreConfig as any).m).reduce((obj: any, [key, value]) => {
obj[key] = value;
return obj;
}, {});
return config;
}
export function setConfig(config: IonicConfig) {
const coreConfig = getCoreConfig();
coreConfig.reset(config);
}
function getCoreConfig(): Config {
if (typeof (window as any) !== 'undefined') {
const Ionic = window.Ionic;
if (Ionic && Ionic.config) {
return window.Ionic.config;
}
}
return null;
}

View File

@ -8,3 +8,5 @@ export function generateUniqueId() {
}
export * from './attachEventProps';
export * from './IonicContext';
export * from './utilHooks';

View File

@ -0,0 +1,140 @@
import { Platforms, getPlatforms, isPlatform } from '@ionic/core';
export class Platform {
constructor() { }
/**
* @returns returns true/false based on platform.
* @description
* Depending on the platform the user is on, `is(platformName)` will
* return `true` or `false`. Note that the same app can return `true`
* for more than one platform name. For example, an app running from
* an iPad would return `true` for the platform names: `mobile`,
* `ios`, `ipad`, and `tablet`. Additionally, if the app was running
* from Cordova then `cordova` would be true, and if it was running
* from a web browser on the iPad then `mobileweb` would be `true`.
*
* ```
* import { Platform } from 'ionic-angular';
*
* @Component({...})
* export MyPage {
* constructor(public platform: Platform) {
* if (this.platform.is('ios')) {
* // This will only print when on iOS
* console.log('I am an iOS device!');
* }
* }
* }
* ```
*
* | Platform Name | Description |
* |-----------------|------------------------------------|
* | android | on a device running Android. |
* | cordova | on a device running Cordova. |
* | ios | on a device running iOS. |
* | ipad | on an iPad device. |
* | iphone | on an iPhone device. |
* | phablet | on a phablet device. |
* | tablet | on a tablet device. |
* | electron | in Electron on a desktop device. |
* | pwa | as a PWA app. |
* | mobile | on a mobile device. |
* | mobileweb | on a mobile device in a browser. |
* | desktop | on a desktop device. |
* | hybrid | is a cordova or capacitor app. |
*
*/
is(platformName: Platforms) {
return isPlatform(window, platformName);
}
/**
* @returns the array of platforms
* @description
* Depending on what device you are on, `platforms` can return multiple values.
* Each possible value is a hierarchy of platforms. For example, on an iPhone,
* it would return `mobile`, `ios`, and `iphone`.
*
* ```
* import { Platform } from 'ionic-angular';
*
* @Component({...})
* export MyPage {
* constructor(public platform: Platform) {
* // This will print an array of the current platforms
* console.log(this.platform.platforms());
* }
* }
* ```
*/
platforms() {
return getPlatforms(window);
}
/**
* Returns if this app is using right-to-left language direction or not.
* We recommend the app's `index.html` file already has the correct `dir`
* attribute value set, such as `<html dir="ltr">` or `<html dir="rtl">`.
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
*/
get isRTL(): boolean {
return document.dir === 'rtl';
}
/**
* Get the query string parameter
*/
getQueryParam(key: string): string | null {
return readQueryParam(window.location.href, key);
}
/**
* Returns `true` if the app is in landscape mode.
*/
isLandscape(): boolean {
return !this.isPortrait();
}
/**
* Returns `true` if the app is in portait mode.
*/
isPortrait(): boolean {
return window.matchMedia && window.matchMedia('(orientation: portrait)').matches;
}
testUserAgent(expression: string): boolean {
const nav = window.navigator;
return !!(nav && nav.userAgent && nav.userAgent.indexOf(expression) >= 0);
}
/**
* Get the current url.
*/
url() {
return window.location.href;
}
/**
* Gets the width of the platform's viewport using `window.innerWidth`.
*/
width() {
return window.innerWidth;
}
/**
* Gets the height of the platform's viewport using `window.innerHeight`.
*/
height(): number {
return window.innerHeight;
}
}
const readQueryParam = (url: string, key: string) => {
key = key.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
const regex = new RegExp('[\\?&]' + key + '=([^&#]*)');
const results = regex.exec(url);
return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null;
};

View File

@ -0,0 +1,10 @@
import React from 'react';
import { IonicContext } from './IonicContext';
import { IonicConfig } from '@ionic/core';
export const useIonicConfig = () => {
const value = React.useContext(IonicContext);
const config = value.getConfig() || {};
const hook: [IonicConfig, (config: IonicConfig) => void] = [config, value.setConfig];
return hook;
}

View File

@ -0,0 +1,8 @@
export const dashToPascalCase = (str: string) => str.toLowerCase().split('-').map(segment => segment.charAt(0).toUpperCase() + segment.slice(1)).join('');
export function generateUniqueId() {
return ([1e7].toString() + -1e3.toString() + -4e3.toString() + -8e3.toString() + -1e11.toString()).replace(/[018]/g, function(c: any) {
const random = crypto.getRandomValues(new Uint8Array(1)) as Uint8Array;
return (c ^ random[0] & 15 >> c / 4).toString(16);
});
}

4
react/src/globals.ts Normal file
View File

@ -0,0 +1,4 @@
interface Window {
cordova: any;
Ionic: any;
}

View File

@ -1,5 +1,3 @@
export * from './components';
export * from './types';
export * from './lifecycle';
export { setupConfig } from '@ionic/core';