|
1 |
| -import { UIViewData } from '@uirouter/angularjs/lib/directives/viewDirective'; |
2 |
| -import { UIRouterConsumer, UIRouterProvider, UIRouterReact, UIViewConsumer, UIViewProvider } from '@uirouter/react'; |
3 | 1 | import * as angular from 'angular';
|
4 | 2 | import * as React from 'react';
|
5 |
| -import { ReactElement } from 'react'; |
6 |
| -import IInjectorService = angular.auto.IInjectorService; |
| 3 | +import { ReactNode, useContext, useRef, useState } from 'react'; |
| 4 | +import { UIViewData } from '@uirouter/angularjs/lib/directives/viewDirective'; |
| 5 | +import { UIRouterContext, UIViewContext, UIViewAddress } from '@uirouter/react'; |
| 6 | +import { UIRouter } from '@uirouter/core'; |
7 | 7 |
|
8 | 8 | export interface IUIRouterContextComponentProps {
|
9 | 9 | parentContextLevel?: string;
|
10 | 10 | inherited?: boolean;
|
11 | 11 | }
|
12 | 12 |
|
13 |
| -export interface IUIRouterContextComponentState { |
14 |
| - ready: boolean; |
15 |
| - router?: UIRouterReact; |
16 |
| - parentUIViewAddress?: any; |
| 13 | +interface IContextFromAngularJS { |
| 14 | + router: UIRouter; |
| 15 | + addr: UIViewAddress; |
17 | 16 | }
|
| 17 | +const initialState: IContextFromAngularJS = { router: undefined, addr: undefined }; |
18 | 18 |
|
19 | 19 | /**
|
20 | 20 | * Provide react context necessary for UIView, UISref and UISrefActive
|
21 | 21 | *
|
22 | 22 | * Gets the context from the parent react UIView (if component tree is all react)
|
23 | 23 | * Gets the context from the from parent angular ui-view if no parent reat UIView is available
|
24 | 24 | */
|
25 |
| -export class UIRouterContextComponent extends React.Component< |
26 |
| - IUIRouterContextComponentProps, |
27 |
| - IUIRouterContextComponentState |
28 |
| -> { |
29 |
| - public static defaultProps: Partial<IUIRouterContextComponentProps> = { |
30 |
| - parentContextLevel: '0', |
31 |
| - inherited: true, |
32 |
| - }; |
33 |
| - |
34 |
| - public state: IUIRouterContextComponentState = { |
35 |
| - ready: false, |
36 |
| - router: null, |
37 |
| - parentUIViewAddress: null, |
38 |
| - }; |
39 |
| - |
40 |
| - private ref: HTMLElement; |
41 |
| - private injector: IInjectorService; |
42 |
| - |
43 |
| - private getRouterFromAngularJS(): UIRouterReact { |
44 |
| - if (this.injector) return this.injector.get('$uiRouter'); |
45 |
| - } |
46 |
| - |
47 |
| - private getParentViewFromAngularJS() { |
48 |
| - let ref = this.ref; |
49 |
| - let steps = parseInt(this.props.parentContextLevel); |
50 |
| - steps = isNaN(steps) ? 0 : steps; |
51 |
| - |
52 |
| - while (ref && steps--) ref = ref.parentElement; |
53 |
| - const $uiView = ref && angular.element(ref).inheritedData('$uiView'); |
54 |
| - return $uiView && new ParentUIViewAddressAdapter($uiView); |
55 |
| - } |
56 |
| - |
57 |
| - public componentDidMount() { |
58 |
| - this.setState({ |
59 |
| - parentUIViewAddress: this.getParentViewFromAngularJS(), |
60 |
| - }); |
61 |
| - } |
62 |
| - |
63 |
| - private refCallback = (ref: HTMLElement) => { |
64 |
| - if (ref && ref !== this.ref) { |
65 |
| - this.ref = ref; |
66 |
| - this.injector = angular.element(ref).injector(); |
67 |
| - const router = this.getRouterFromAngularJS(); |
68 |
| - const parentUIViewAddress = this.getParentViewFromAngularJS(); |
69 |
| - this.setState({ ready: true, router, parentUIViewAddress }); |
70 |
| - // console.log(ref); |
| 25 | +export function UIRouterContextComponent(props: { |
| 26 | + parentContextLevel: string; |
| 27 | + inherited: boolean; |
| 28 | + children: ReactNode; |
| 29 | +}) { |
| 30 | + const { parentContextLevel, inherited, children } = props; |
| 31 | + const [contextFromAngularJS, setContextFromAngularJS] = useState(initialState); |
| 32 | + const routerFromReactContext = useContext(UIRouterContext); |
| 33 | + const parentUIViewFromReactContext = useContext(UIViewContext); |
| 34 | + const domRef = useRef(null); |
| 35 | + |
| 36 | + // Once we have a DOM node, get the AngularJS injector and walk up the DOM to find the AngularJS $uiView |
| 37 | + const refCallback = (el: HTMLElement) => { |
| 38 | + if (el && el !== domRef.current) { |
| 39 | + domRef.current = el; |
| 40 | + const injector = angular.element(el).injector(); |
| 41 | + |
| 42 | + // get router from angularjs |
| 43 | + const router = injector?.get('$uiRouter'); |
| 44 | + |
| 45 | + // get parent uiview from angularjs |
| 46 | + let steps = parseInt(parentContextLevel, 10); |
| 47 | + steps = isNaN(steps) ? 0 : steps; |
| 48 | + |
| 49 | + while (el && steps--) el = el.parentElement; |
| 50 | + const $uiView = el && angular.element(el).inheritedData('$uiView'); |
| 51 | + const addr = $uiView && new ParentUIViewAddressAdapter($uiView); |
| 52 | + |
| 53 | + setContextFromAngularJS({ router, addr } as IContextFromAngularJS); |
71 | 54 | }
|
72 | 55 | };
|
73 | 56 |
|
74 |
| - private renderChild(child: ReactElement<any>) { |
75 |
| - // console.log('renderChild()', child); |
76 |
| - const inherited = this.props.inherited; |
77 |
| - return ( |
78 |
| - <UIRouterConsumer> |
79 |
| - {routerFromReactContext => ( |
80 |
| - <UIRouterProvider value={inherited && routerFromReactContext || this.state.router}> |
81 |
| - <UIViewConsumer> |
82 |
| - {parentUIViewFromReactContext => ( |
83 |
| - <UIViewProvider value={inherited && parentUIViewFromReactContext || this.state.parentUIViewAddress}> |
84 |
| - {child} |
85 |
| - </UIViewProvider> |
86 |
| - )} |
87 |
| - </UIViewConsumer> |
88 |
| - </UIRouterProvider> |
89 |
| - )} |
90 |
| - </UIRouterConsumer> |
91 |
| - ); |
| 57 | + // render a div once to get a ref into the dom |
| 58 | + // Use the dom ref to access the AngularJS state |
| 59 | + if (!domRef.current) { |
| 60 | + return <div ref={refCallback} />; |
92 | 61 | }
|
93 | 62 |
|
94 |
| - public render() { |
95 |
| - const { ready } = this.state; |
96 |
| - const { children } = this.props; |
| 63 | + // We know the AngularJS state. Now render the {children} |
| 64 | + const childrenCount = React.Children.count(children); |
97 | 65 |
|
98 |
| - const childrenCount = React.Children.count(children); |
99 |
| - if (!ready) { |
100 |
| - return <div ref={this.refCallback} />; |
101 |
| - } |
102 |
| - |
103 |
| - return this.renderChild(childrenCount === 1 ? React.Children.only(children) : <div>{children}</div>); |
104 |
| - } |
| 66 | + return ( |
| 67 | + <UIRouterContext.Provider value={(inherited && routerFromReactContext) || contextFromAngularJS.router}> |
| 68 | + <UIViewContext.Provider value={(inherited && parentUIViewFromReactContext) || contextFromAngularJS.addr}> |
| 69 | + {childrenCount === 1 ? React.Children.only(children) : <div>{children}</div>} |
| 70 | + </UIViewContext.Provider> |
| 71 | + </UIRouterContext.Provider> |
| 72 | + ); |
105 | 73 | }
|
106 | 74 |
|
| 75 | +UIRouterContextComponent.defaultProps = { |
| 76 | + parentContextLevel: '0', |
| 77 | + inherited: true, |
| 78 | +} as IUIRouterContextComponentProps; |
| 79 | + |
107 | 80 | /**
|
108 | 81 | * Get the fqn and context from the parent angularjs ui-view.
|
109 | 82 | * Uses the ui-view element's data
|
|
0 commit comments