Skip to content

Commit

Permalink
feat(*): Support UI-Router 0.8.2 and switch to React 16.3 context API
Browse files Browse the repository at this point in the history
BREAKING CHANGE: This version of react-hybrid requires `@uirouter/react` version 0.8.2 or higher and requires React 16.3.x or higher.
  • Loading branch information
christopherthielen committed May 28, 2018
1 parent a3cb5ce commit 09af7b6
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 132 deletions.
20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,30 @@
},
"license": "MIT",
"dependencies": {
"@uirouter/angularjs": "1.0.16",
"@uirouter/core": "5.0.18",
"@uirouter/react": "0.7.0"
"@uirouter/angularjs": "1.0.18",
"@uirouter/core": "5.0.19",
"@uirouter/react": "0.8.2"
},
"peerDependencies": {
"angular": "^1.5.0",
"react": "^15.0.0"
},
"devDependencies": {
"@types/angular": "^1.6.45",
"@types/jquery": "^3.3.0",
"@types/react": "^16.3.13",
"@uirouter/publish-scripts": "^2.2.10",
"angular": "^1.6.8",
"@types/jquery": "^3.3.2",
"@types/react": "^16.3.14",
"@uirouter/publish-scripts": "^2.3.18",
"angular": "^1.7.0",
"husky": "^0.14.3",
"prettier": "^1.12.1",
"pretty-quick": "^1.4.1",
"pretty-quick": "^1.6.0",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"rollup": "^0.58.2",
"rollup": "^0.59.2",
"rollup-plugin-commonjs": "^9.1.3",
"rollup-plugin-node-resolve": "^3.0.2",
"rollup-plugin-sourcemaps": "^0.4.2",
"rollup-plugin-uglify": "^3.0.0",
"rollup-plugin-uglify": "^4.0.0",
"rxjs": "^5.5.6",
"typescript": "^2.8.1"
},
Expand Down
2 changes: 1 addition & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import nodeResolve from 'rollup-plugin-node-resolve';
import uglify from 'rollup-plugin-uglify';
import { uglify } from 'rollup-plugin-uglify';
import sourcemaps from 'rollup-plugin-sourcemaps';
import commonjs from 'rollup-plugin-commonjs';

Expand Down
15 changes: 12 additions & 3 deletions src/angularjs/ReactUIViewAdapterComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import * as React from 'react';
import * as angular from 'angular';
import * as ReactDOM from 'react-dom';
import { hybridModule } from './module';
import { UIView, UIViewProps } from '@uirouter/react';
import { UIView, UIViewProps, UIRouterConsumer, UIViewConsumer } from '@uirouter/react';
import { filter } from '@uirouter/core';
import { UIRouterContextComponent } from '../react/UIRouterReactContext';

// When an angularjs `ui-view` is instantiated, also create an adapter (which creates a react UIView)
// When an angularjs `ui-view` is instantiated, also create an react-ui-view-adapter (which creates a react UIView)
hybridModule.directive('uiView', function() {
return {
restrict: 'AE',
Expand Down Expand Up @@ -66,6 +66,7 @@ hybridModule.directive('reactUiViewAdapter', function() {
const provideContextToAngularJSChildren = () => {
const $cfg = _ref && _ref.uiViewData && _ref.uiViewData.config;
const $uiView = _ref && _ref.uiViewAddress;
// console.log(`${$id}: providing context to angularjs children`, el, $cfg, $uiView);
if (!$cfg || !$uiView) {
elem.removeData('$uiView');
} else {
Expand Down Expand Up @@ -97,8 +98,16 @@ hybridModule.directive('reactUiViewAdapter', function() {
};
});

const InternalUIView = UIView.__internalViewComponent;

const ReactUIView = ({ refFn, ...props }) => (
<UIRouterContextComponent parentContextLevel="3">
<UIView {...props} ref={refFn} />
<UIRouterConsumer>
{router => (
<UIViewConsumer>
{parentUiView => <InternalUIView {...props} ref={refFn} parentUIView={parentUiView} router={router} />}
</UIViewConsumer>
)}
</UIRouterConsumer>
</UIRouterContextComponent>
);
92 changes: 42 additions & 50 deletions src/react/UIRouterReactContext.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import * as angular from 'angular';
import { UIViewData } from '@uirouter/angularjs/lib/directives/viewDirective';
import { UIRouter } from '@uirouter/core';

import { UIRouterConsumer, UIRouterProvider, UIRouterReact, UIViewConsumer, UIViewProvider } from '@uirouter/react';
import * as angular from 'angular';
import * as React from 'react';
import { ReactElement } from 'react';
import IInjectorService = angular.auto.IInjectorService;

export interface IUIRouterContextComponentProps {
parentContextLevel?: string;
}

export interface IUIRouterContextComponentState {
router: UIRouter;
parentUIViewAddress: any;
ready: boolean;
router?: UIRouterReact;
parentUIViewAddress?: any;
}

/**
Expand All @@ -25,87 +25,79 @@ export class UIRouterContextComponent extends React.Component<
IUIRouterContextComponentProps,
IUIRouterContextComponentState
> {
// context from parent react UIView
public static contextTypes = {
router: PropTypes.object,
parentUIViewAddress: PropTypes.object,
};

// context to child
public static childContextTypes = {
router: PropTypes.object,
parentUIViewAddress: PropTypes.object,
};

public static defaultProps: Partial<IUIRouterContextComponentProps> = {
parentContextLevel: '0',
};

public state: IUIRouterContextComponentState = {
ready: false,
router: null,
parentUIViewAddress: null,
};

private ref: HTMLElement;
private injector: IInjectorService;

private getRouter() {
// from react
if (this.context.router) {
return this.context.router;
}

// from angular
private getRouterFromAngularJS(): UIRouterReact {
if (this.injector) return this.injector.get('$uiRouter');
}

private getParentView() {
// from react
if (this.context.parentUIViewAddress) {
return this.context.parentUIViewAddress;
}

// from angular
private getParentViewFromAngularJS() {
let ref = this.ref;
let steps = parseInt(this.props.parentContextLevel);
steps = isNaN(steps) ? 0 : steps;

while (steps--) ref = ref.parentElement;
while (ref && steps--) ref = ref.parentElement;
const $uiView = ref && angular.element(ref).inheritedData('$uiView');
return $uiView && $uiView && new ParentUIViewAddressAdapter($uiView);
}

public getChildContext() {
return {
router: this.state.router,
parentUIViewAddress: this.state.parentUIViewAddress,
};
return $uiView && new ParentUIViewAddressAdapter($uiView);
}

public componentDidMount() {
this.setState({
router: this.getRouter(),
parentUIViewAddress: this.getParentView(),
parentUIViewAddress: this.getParentViewFromAngularJS(),
});
}

private refCallback = (ref: HTMLElement) => {
if (ref && ref !== this.ref) {
this.ref = ref;
this.injector = angular.element(ref).injector();
this.setState({});
// Add $uiView data
const router = this.getRouterFromAngularJS();
const parentUIViewAddress = this.getParentViewFromAngularJS();
this.setState({ ready: true, router, parentUIViewAddress });
// console.log(ref);
}
};

private renderChild(child: ReactElement<any>) {
// console.log('renderChild()', child);
return (
<UIRouterConsumer>
{routerFromReactContext => (
<UIRouterProvider value={routerFromReactContext || this.state.router}>
<UIViewConsumer>
{parentUIViewFromReactContext => (
<UIViewProvider value={parentUIViewFromReactContext || this.state.parentUIViewAddress}>
{child}
</UIViewProvider>
)}
</UIViewConsumer>
</UIRouterProvider>
)}
</UIRouterConsumer>
);
}

public render() {
const { router } = this.state;
const { ready } = this.state;
const { children } = this.props;

const ready = !!router;
const childrenCount = React.Children.count(children);
const child = ready && (childrenCount === 1 ? React.Children.only(children) : <div>{children}</div>);
return this.ref ? child : <div ref={this.refCallback} />;
if (!ready) {
return <div ref={this.refCallback} />;
}

return this.renderChild(childrenCount === 1 ? React.Children.only(children) : <div>{children}</div>);
}
}

Expand Down
14 changes: 0 additions & 14 deletions src/react/UIViewMonkeyPatch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import { AngularUIView } from './AngularUIView';
* </ui-view>
*/
const realRender = UIView.prototype.render;
const realComponentWillMount = UIView.prototype.componentWillMount;
const realComponentWillUnmount = UIView.prototype.componentWillUnmount;

UIView.prototype.render = function() {
if (this.props.wrap === false) {
Expand All @@ -29,15 +27,3 @@ UIView.prototype.render = function() {

return <AngularUIView {...this.props} />;
};

UIView.prototype.componentWillMount = function() {
if (this.props.wrap === false) {
return realComponentWillMount.apply(this, arguments);
}
};

UIView.prototype.componentWillUnmount = function() {
if (this.props.wrap === false) {
return realComponentWillUnmount.apply(this, arguments);
}
};
Loading

0 comments on commit 09af7b6

Please sign in to comment.