mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-16 10:01:59 +08:00
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:
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ionic/react",
|
"name": "@ionic/react",
|
||||||
"version": "0.0.6-3",
|
"version": "0.0.6",
|
||||||
"description": "React specific wrapper for @ionic/core",
|
"description": "React specific wrapper for @ionic/core",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"ionic",
|
"ionic",
|
||||||
@ -36,14 +36,15 @@
|
|||||||
"dist/"
|
"dist/"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ionic/core": "^4.6.0-dev.201906192117.6727cfc",
|
"@ionic/core": "^4.6.0",
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
"react-router": "^5.0.1",
|
"react-router": "^5.0.1",
|
||||||
"react-router-dom": "^5.0.1"
|
"react-router-dom": "^5.0.1",
|
||||||
|
"ionicons": "^4.5.10-2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trust/webcrypto": "^0.9.2",
|
"@trust/webcrypto": "^0.9.2",
|
||||||
@ -53,6 +54,7 @@
|
|||||||
"@types/react-dom": "^16.8.4",
|
"@types/react-dom": "^16.8.4",
|
||||||
"@types/react-router": "^4.4.4",
|
"@types/react-router": "^4.4.4",
|
||||||
"@types/react-router-dom": "^4.3.1",
|
"@types/react-router-dom": "^4.3.1",
|
||||||
|
"ionicons": "^4.6.0",
|
||||||
"jest": "^24.8.0",
|
"jest": "^24.8.0",
|
||||||
"jest-dom": "^3.4.0",
|
"jest-dom": "^3.4.0",
|
||||||
"np": "^5.0.1",
|
"np": "^5.0.1",
|
||||||
|
37
react/src/components/IonApp.tsx
Normal file
37
react/src/components/IonApp.tsx
Normal 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;
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import ReactDom from 'react-dom';
|
import ReactDom from 'react-dom';
|
||||||
import { dashToPascalCase, attachEventProps } from './utils';
|
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);
|
const displayName = dashToPascalCase(tagName);
|
||||||
|
|
||||||
type IonicReactInternalProps = {
|
type IonicReactInternalProps = {
|
||||||
@ -17,11 +17,9 @@ export function createReactComponent<PropType, ElementType>(tagName: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ReactComponent extends React.Component<InternalProps> {
|
class ReactComponent extends React.Component<InternalProps> {
|
||||||
componentRef: React.RefObject<ElementType>;
|
|
||||||
|
|
||||||
constructor(props: PropType & IonicReactInternalProps) {
|
constructor(props: PropType & IonicReactInternalProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.componentRef = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get displayName() {
|
static get displayName() {
|
||||||
@ -40,10 +38,17 @@ export function createReactComponent<PropType, ElementType>(tagName: string) {
|
|||||||
render() {
|
render() {
|
||||||
const { children, forwardedRef, ...cProps } = this.props;
|
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(
|
return React.createElement(
|
||||||
tagName,
|
tagName,
|
||||||
{
|
{
|
||||||
...cProps,
|
...propsWithoutAttributeValues,
|
||||||
ref: forwardedRef
|
ref: forwardedRef
|
||||||
},
|
},
|
||||||
children
|
children
|
||||||
|
@ -63,4 +63,3 @@ export function createControllerComponent<OptionsType extends object, LoadingEle
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
import { defineCustomElements } from '@ionic/core/loader';
|
import { defineCustomElements } from '@ionic/core/loader';
|
||||||
export { AlertButton, AlertInput } from '@ionic/core';
|
export { AlertButton, AlertInput } from '@ionic/core';
|
||||||
|
export { IonApp } from './IonApp'
|
||||||
export * from './proxies';
|
export * from './proxies';
|
||||||
|
|
||||||
// createControllerComponent
|
// createControllerComponent
|
||||||
@ -17,8 +18,38 @@ export { IonPopover } from './IonPopover';
|
|||||||
export { IonPage } from './IonPage';
|
export { IonPage } from './IonPage';
|
||||||
export { IonTabs } from './navigation/IonTabs';
|
export { IonTabs } from './navigation/IonTabs';
|
||||||
export { IonTabBar } from './navigation/IonTabBar';
|
export { IonTabBar } from './navigation/IonTabBar';
|
||||||
export { IonRouterOutlet } from './navigation/IonRouterOutlet';
|
|
||||||
export { IonBackButton } from './navigation/IonBackButton';
|
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);
|
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,
|
||||||
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { JSX as LocalJSX } from '@ionic/core';
|
import { JSX as LocalJSX } from '@ionic/core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { NavContext } from './NavContext';
|
import { NavContext } from './routing/NavContext';
|
||||||
import { IonBackButtonInner } from '../proxies';
|
import { IonBackButtonInner } from '../proxies';
|
||||||
|
|
||||||
type BackButtonProps = LocalJSX.IonBackButton & {
|
type BackButtonProps = LocalJSX.IonBackButton & {
|
||||||
|
@ -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);
|
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IonTabBar } from './IonTabBar';
|
import { IonTabBar } from './IonTabBar';
|
||||||
import { IonRouterOutlet } from './IonRouterOutlet';
|
import { IonRouterOutlet } from './routing/IonRouterOutlet';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@ -25,7 +25,6 @@ const tabsInner: React.CSSProperties = {
|
|||||||
contain: 'layout size style'
|
contain: 'layout size style'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export class IonTabs extends React.Component<Props> {
|
export class IonTabs extends React.Component<Props> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IonLifeCycleContext } from '../../lifecycle';
|
import { IonLifeCycleContext } from '../../lifecycle';
|
||||||
import { DefaultIonLifeCycleContext } from '../../lifecycle/IonLifeCycleContext';
|
import { DefaultIonLifeCycleContext } from '../../lifecycle/IonLifeCycleContext';
|
||||||
import { NavContext } from './NavContext';
|
import { NavContext } from './routing/NavContext';
|
||||||
|
|
||||||
interface StackItemManagerProps {
|
interface StackItemManagerProps {
|
||||||
id: string;
|
id: string;
|
||||||
|
70
react/src/components/navigation/routing/IonRouterOutlet.tsx
Normal file
70
react/src/components/navigation/routing/IonRouterOutlet.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,10 @@
|
|||||||
import React from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import { ViewItem } from './ViewItem';
|
import { ViewItem } from './ReactRouter/ViewItem';
|
||||||
import { NavDirection } from '@ionic/core';
|
import { NavDirection } from '@ionic/core';
|
||||||
import { Location } from 'history';
|
|
||||||
|
|
||||||
|
|
||||||
export interface ViewStack {
|
export interface ViewStack {
|
||||||
routerOutlet: HTMLIonRouterOutletElement;
|
routerOutlet: HTMLIonRouterOutletElement;
|
||||||
activeId?: string,
|
activeId?: string,
|
||||||
// prevId?: string,
|
|
||||||
views: ViewItem[]
|
views: ViewItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,8 +15,9 @@ export interface ViewStacks {
|
|||||||
export interface NavContextState {
|
export interface NavContextState {
|
||||||
hideView: (viewId: string) => void;
|
hideView: (viewId: string) => void;
|
||||||
viewStacks: ViewStacks;
|
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;
|
removeViewStack: (stack: string) => void;
|
||||||
|
renderChild: (item: ViewItem) => void;
|
||||||
goBack: (defaultHref?: string) => void;
|
goBack: (defaultHref?: string) => void;
|
||||||
transitionView: (enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOuter: HTMLIonRouterOutletElement, direction: NavDirection) => void;
|
transitionView: (enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOuter: HTMLIonRouterOutletElement, direction: NavDirection) => void;
|
||||||
}
|
}
|
||||||
@ -28,8 +26,9 @@ export const NavContext = /*@__PURE__*/React.createContext<NavContextState>({
|
|||||||
viewStacks: {},
|
viewStacks: {},
|
||||||
hideView: () => { navContextNotFoundError(); },
|
hideView: () => { navContextNotFoundError(); },
|
||||||
goBack: () => { navContextNotFoundError(); },
|
goBack: () => { navContextNotFoundError(); },
|
||||||
registerViewStack: () => { navContextNotFoundError(); },
|
setupIonRouter: () => { navContextNotFoundError() },
|
||||||
removeViewStack: () => { navContextNotFoundError(); },
|
removeViewStack: () => { navContextNotFoundError(); },
|
||||||
|
renderChild: () => { navContextNotFoundError(); },
|
||||||
transitionView: () => { navContextNotFoundError(); }
|
transitionView: () => { navContextNotFoundError(); }
|
||||||
});
|
});
|
||||||
|
|
@ -1,26 +1,32 @@
|
|||||||
import React from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import { withRouter, RouteComponentProps, matchPath, match, Redirect } from 'react-router-dom';
|
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 { 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 { ViewItem } from './ViewItem';
|
||||||
import { NavDirection } from '@ionic/core';
|
import { NavDirection } from '@ionic/core';
|
||||||
import { generateUniqueId } from '../utils';
|
import { generateUniqueId } from '../../../utils';
|
||||||
|
|
||||||
interface IonRouterProps extends RouteComponentProps { }
|
interface IonReactRouterProps extends RouteComponentProps { }
|
||||||
interface IonRouterState extends NavContextState { }
|
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;
|
listenUnregisterCallback: UnregisterCallback;
|
||||||
activeViewId?: string;
|
activeViewId?: string;
|
||||||
prevViewId?: string;
|
prevViewId?: string;
|
||||||
|
|
||||||
constructor(props: IonRouterProps) {
|
constructor(props: IonReactRouterProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
viewStacks: {},
|
viewStacks: {},
|
||||||
hideView: this.hideView.bind(this),
|
hideView: this.hideView.bind(this),
|
||||||
registerViewStack: this.registerView.bind(this),
|
setupIonRouter: this.setupIonRouter.bind(this),
|
||||||
removeViewStack: this.removeViewStack.bind(this),
|
removeViewStack: this.removeViewStack.bind(this),
|
||||||
|
renderChild: this.renderChild.bind(this),
|
||||||
goBack: this.goBack.bind(this),
|
goBack: this.goBack.bind(this),
|
||||||
transitionView: this.transitionView.bind(this)
|
transitionView: this.transitionView.bind(this)
|
||||||
};
|
};
|
||||||
@ -47,14 +53,14 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
findViewInfoByLocation(location: HistoryLocation, viewStacks: ViewStacks) {
|
findViewInfoByLocation(location: HistoryLocation, viewStacks: ViewStacks) {
|
||||||
let view: ViewItem;
|
let view: ViewItem<IonRouteData>;
|
||||||
let match: match<{ tab: string }>;
|
let match: IonRouteData["match"];
|
||||||
let viewStack: ViewStack;
|
let viewStack: ViewStack;
|
||||||
const keys = Object.keys(viewStacks);
|
const keys = Object.keys(viewStacks);
|
||||||
keys.some(key => {
|
keys.some(key => {
|
||||||
const vs = viewStacks[key];
|
const vs = viewStacks[key];
|
||||||
return vs.views.some(x => {
|
return vs.views.some(x => {
|
||||||
match = matchPath(location.pathname, x.childProps)
|
match = matchPath(location.pathname, x.routeData.childProps)
|
||||||
if (match) {
|
if (match) {
|
||||||
view = x;
|
view = x;
|
||||||
viewStack = vs;
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
findViewInfoById(id: string, viewStacks: ViewStacks) {
|
findViewInfoById(id: string, viewStacks: ViewStacks) {
|
||||||
let view: ViewItem;
|
let view: ViewItem<IonRouteData>;
|
||||||
let viewStack: ViewStack;
|
let viewStack: ViewStack;
|
||||||
const keys = Object.keys(viewStacks);
|
const keys = Object.keys(viewStacks);
|
||||||
keys.some(key => {
|
keys.some(key => {
|
||||||
@ -96,7 +102,7 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
|
|||||||
|
|
||||||
const { view: leavingView } = this.findViewInfoById(this.activeViewId, viewStacks);
|
const { view: leavingView } = this.findViewInfoById(this.activeViewId, viewStacks);
|
||||||
|
|
||||||
if (leavingView && leavingView.match.url === location.pathname) {
|
if (leavingView && leavingView.routeData.match.url === location.pathname) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,13 +117,13 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
|
|||||||
|
|
||||||
enteringView.show = true;
|
enteringView.show = true;
|
||||||
enteringView.mount = true;
|
enteringView.mount = true;
|
||||||
enteringView.match = match;
|
enteringView.routeData.match = match;
|
||||||
enteringViewStack.activeId = enteringView.id;
|
enteringViewStack.activeId = enteringView.id;
|
||||||
this.activeViewId = enteringView.id;
|
this.activeViewId = enteringView.id;
|
||||||
|
|
||||||
if (leavingView) {
|
if (leavingView) {
|
||||||
this.prevViewId = leavingView.id
|
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') {
|
if (action === 'PUSH') {
|
||||||
direction = direction || 'forward';
|
direction = direction || 'forward';
|
||||||
} else {
|
} else {
|
||||||
@ -155,7 +161,50 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
|
|||||||
this.listenUnregisterCallback();
|
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) => {
|
this.setState((prevState) => {
|
||||||
const prevViewStacks = Object.assign({}, prevState.viewStacks);
|
const prevViewStacks = Object.assign({}, prevState.viewStacks);
|
||||||
prevViewStacks[stack] = {
|
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[]) {
|
findActiveView(views: ViewItem[]) {
|
||||||
let view: ViewItem | undefined;
|
let view: ViewItem<IonRouteData> | undefined;
|
||||||
views.some(x => {
|
views.some(x => {
|
||||||
const match = matchPath(this.props.location.pathname, x.childProps)
|
const match = matchPath(this.props.location.pathname, x.routeData.childProps)
|
||||||
if (match) {
|
if (match) {
|
||||||
view = x;
|
view = x;
|
||||||
return true;
|
return true;
|
||||||
@ -209,7 +265,7 @@ class IonRouter extends React.Component<IonRouterProps, IonRouterState> {
|
|||||||
if (leavingView) {
|
if (leavingView) {
|
||||||
const { view: enteringView } = this.findViewInfoById(leavingView.prevId, this.state.viewStacks);
|
const { view: enteringView } = this.findViewInfoById(leavingView.prevId, this.state.viewStacks);
|
||||||
if (enteringView) {
|
if (enteringView) {
|
||||||
this.props.history.replace(enteringView.match.url, { direction: 'back' });
|
this.props.history.replace(enteringView.routeData.match.url, { direction: 'back' });
|
||||||
} else {
|
} else {
|
||||||
this.props.history.replace(defaultHref, { direction: 'back' });
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,10 @@
|
|||||||
import { match, RouteProps } from 'react-router-dom';
|
export type ViewItem<RouteData = any> = {
|
||||||
|
|
||||||
export type ViewItem = {
|
|
||||||
id: string;
|
id: string;
|
||||||
key: string;
|
key: string;
|
||||||
match: match<{ tab: string }>;
|
|
||||||
element: React.ReactElement<any>;
|
element: React.ReactElement<any>;
|
||||||
ref?: React.RefObject<HTMLElement>;
|
ref?: React.RefObject<HTMLElement>;
|
||||||
|
routeData: RouteData;
|
||||||
prevId?: string;
|
prevId?: string;
|
||||||
mount: boolean;
|
mount: boolean;
|
||||||
show: boolean;
|
show: boolean;
|
||||||
childProps?: RouteProps;
|
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ import { JSX as IoniconsJSX } from 'ionicons';
|
|||||||
|
|
||||||
|
|
||||||
// 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
|
// /*@__PURE__*/createReactComponent
|
||||||
export const IonTabBarInner = /*@__PURE__*/createReactComponent<JSX.IonTabBar & ReactProps, HTMLIonTabBarElement>('ion-tab-bar');
|
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 IonTab = /*@__PURE__*/createReactComponent<JSX.IonTab & ReactProps, HTMLIonTabElement>('ion-tab');
|
||||||
export const IonTabButton = /*@__PURE__*/createReactComponent<JSX.IonTabButton & ReactProps, HTMLIonTabButtonElement>('ion-tab-button');
|
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 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 IonAvatar = /*@__PURE__*/createReactComponent<JSX.IonAvatar & ReactProps, HTMLIonAvatarElement>('ion-avatar');
|
||||||
export const IonBackdrop = /*@__PURE__*/createReactComponent<JSX.IonBackdrop & ReactProps, HTMLIonBackdropElement>('ion-backdrop');
|
export const IonBackdrop = /*@__PURE__*/createReactComponent<JSX.IonBackdrop & ReactProps, HTMLIonBackdropElement>('ion-backdrop');
|
||||||
export const IonBadge = /*@__PURE__*/createReactComponent<JSX.IonBadge & ReactProps, HTMLIonBadgeElement>('ion-badge');
|
export const IonBadge = /*@__PURE__*/createReactComponent<JSX.IonBadge & ReactProps, HTMLIonBadgeElement>('ion-badge');
|
||||||
|
15
react/src/components/utils/IonicContext.ts
Normal file
15
react/src/components/utils/IonicContext.ts
Normal 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
|
||||||
|
});
|
25
react/src/components/utils/config.ts
Normal file
25
react/src/components/utils/config.ts
Normal 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;
|
||||||
|
}
|
@ -8,3 +8,5 @@ export function generateUniqueId() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export * from './attachEventProps';
|
export * from './attachEventProps';
|
||||||
|
export * from './IonicContext';
|
||||||
|
export * from './utilHooks';
|
||||||
|
140
react/src/components/utils/platform.ts
Normal file
140
react/src/components/utils/platform.ts
Normal 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;
|
||||||
|
};
|
10
react/src/components/utils/utilHooks.ts
Normal file
10
react/src/components/utils/utilHooks.ts
Normal 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;
|
||||||
|
}
|
8
react/src/components/utils/utils.ts
Normal file
8
react/src/components/utils/utils.ts
Normal 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
4
react/src/globals.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
interface Window {
|
||||||
|
cordova: any;
|
||||||
|
Ionic: any;
|
||||||
|
}
|
@ -1,5 +1,3 @@
|
|||||||
export * from './components';
|
export * from './components';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
export * from './lifecycle';
|
export * from './lifecycle';
|
||||||
|
|
||||||
export { setupConfig } from '@ionic/core';
|
|
||||||
|
Reference in New Issue
Block a user