aboutsummaryrefslogtreecommitdiffstats
path: root/sdnr/wt/odlux/framework/src/flux/connect.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'sdnr/wt/odlux/framework/src/flux/connect.tsx')
-rw-r--r--sdnr/wt/odlux/framework/src/flux/connect.tsx213
1 files changed, 213 insertions, 0 deletions
diff --git a/sdnr/wt/odlux/framework/src/flux/connect.tsx b/sdnr/wt/odlux/framework/src/flux/connect.tsx
new file mode 100644
index 000000000..09d30dae7
--- /dev/null
+++ b/sdnr/wt/odlux/framework/src/flux/connect.tsx
@@ -0,0 +1,213 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+import React, { FC, useContext, createContext, useState, useEffect, useRef } from 'react';
+
+import { Dispatch } from './store';
+
+import { ApplicationStore, IApplicationStoreState } from '../store/applicationStore';
+
+const LogLevel = +(localStorage.getItem('log.odlux.framework.flux.connect') || 0);
+
+interface IApplicationStoreContext {
+ applicationStore: ApplicationStore;
+}
+
+export interface IDispatcher {
+ dispatch: Dispatch;
+}
+
+interface IApplicationStoreProps {
+ state: IApplicationStoreState;
+}
+
+interface IDispatchProps {
+ dispatch: Dispatch;
+}
+
+type ComponentDecoratorInfer<TMergedProps> = {
+ <TProps>(wrappedComponent: React.ComponentType<TProps & TMergedProps>): React.ComponentClass<Omit<TProps & TMergedProps, keyof TMergedProps>>;
+};
+
+const ApplicationStoreContext = createContext<IApplicationStoreContext | undefined>(undefined);
+
+export type Connect<TMapProps extends ((...args: any) => any) | undefined = undefined, TMapDispatch extends ((...args: any) => any) | undefined = undefined> =
+ (TMapProps extends ((...args: any) => any) ? ReturnType<TMapProps> : IApplicationStoreProps) &
+ (TMapDispatch extends ((...args: any) => any) ? ReturnType<TMapDispatch> : IDispatchProps);
+
+export function connect(): ComponentDecoratorInfer<IApplicationStoreProps & IDispatchProps>;
+
+export function connect<TStateProps>(
+ mapStateToProps: (state: IApplicationStoreState) => TStateProps
+): ComponentDecoratorInfer<TStateProps & IDispatchProps>;
+
+export function connect<TStateProps, TDispatchProps>(
+ mapStateToProps: (state: IApplicationStoreState) => TStateProps,
+ mapDispatchToProps: (dispatcher: IDispatcher) => TDispatchProps
+): ComponentDecoratorInfer<TStateProps & TDispatchProps>;
+
+
+export function connect<TDispatchProps>(
+ mapStateToProps: undefined,
+ mapDispatchToProps: (dispatcher: IDispatcher) => TDispatchProps
+): ComponentDecoratorInfer<IApplicationStoreProps & TDispatchProps>;
+
+
+export function connect<TProps, TStateProps, TDispatchProps>(
+ mapStateToProps?: ((state: IApplicationStoreState) => TStateProps),
+ mapDispatchToProps?: ((dispatcher: IDispatcher) => TDispatchProps)
+):
+ ((WrappedComponent: React.ComponentType<TProps & (IApplicationStoreProps | TStateProps) & IDispatchProps>) => React.ComponentType<TProps>) {
+
+ const injectApplicationStore = (WrappedComponent: React.ComponentType<TProps & (IApplicationStoreProps | TStateProps) & IDispatchProps>): React.ComponentType<TProps> => {
+
+ class StoreAdapter extends React.Component<TProps, {}> {
+
+ render(): JSX.Element {
+
+ if (isWrappedComponentIsVersion1(WrappedComponent)) {
+ const element = React.createElement(WrappedComponent, { ...(this.props as any), state: this.store.state, dispatch: this.store.dispatch.bind(this.store) });
+ return element;
+ } else if (mapStateToProps && isWrappedComponentIsVersion2(WrappedComponent)) {
+ const element = React.createElement(WrappedComponent, { ...(this.props as any), ...(mapStateToProps(this.store.state) as any), dispatch: this.store.dispatch.bind(this.store) });
+ return element;
+ } else if (mapStateToProps && mapDispatchToProps && isWrappedComponentIsVersion3(WrappedComponent)) {
+ const element = React.createElement(WrappedComponent, { ...(this.props as any), ...(mapStateToProps(this.store.state) as any), ...(mapDispatchToProps({ dispatch: this.store.dispatch.bind(this.store) }) as any) });
+ return element;
+ } else if (!mapStateToProps && mapDispatchToProps && isWrappedComponentIsVersion4(WrappedComponent)) {
+ const element = React.createElement(WrappedComponent, { ...(this.props as any), state: this.store.state, ...(mapDispatchToProps({ dispatch: this.store.dispatch.bind(this.store) }) as any) });
+ return element;
+ }
+ throw new Error("Invalid arguments in connect.");
+ }
+
+ componentDidMount(): void {
+ this.store && this.store.changed.addHandler(this.handleStoreChanged);
+ }
+
+ componentWillUnmount(): void {
+ this.store && this.store.changed.removeHandler(this.handleStoreChanged);
+ }
+
+ private get store(): ApplicationStore {
+ return this.context.applicationStore;
+ }
+
+ private handleStoreChanged = () => {
+ this.forceUpdate();
+ }
+ }
+ StoreAdapter.contextType = ApplicationStoreContext;
+ return StoreAdapter;
+ }
+
+
+ return injectApplicationStore;
+
+ /* inline methods */
+
+ function isWrappedComponentIsVersion1(wrappedComponent: any): wrappedComponent is React.ComponentType<TProps & IApplicationStoreProps & IDispatchProps> {
+ return !mapStateToProps && !mapDispatchToProps;
+ }
+
+ function isWrappedComponentIsVersion2(wrappedComponent: any): wrappedComponent is React.ComponentType<TProps & TStateProps & IDispatchProps> {
+ return !!mapStateToProps && !mapDispatchToProps;
+ }
+
+ function isWrappedComponentIsVersion3(wrappedComponent: any): wrappedComponent is React.ComponentType<TProps & TStateProps & TDispatchProps> {
+ return !!mapStateToProps && !!mapDispatchToProps;
+ }
+
+ function isWrappedComponentIsVersion4(wrappedComponent: any): wrappedComponent is React.ComponentType<TProps & TStateProps & TDispatchProps> {
+ return !mapStateToProps && !!mapDispatchToProps;
+ }
+}
+
+type ApplicationStoreProviderProps = {
+ applicationStore: ApplicationStore;
+}
+
+export const ApplicationStoreProvider: FC<ApplicationStoreProviderProps> = (props) => {
+ const { applicationStore, children } = props;
+
+ return (
+ <ApplicationStoreContext.Provider value={{ applicationStore }}>
+ {children}
+ </ApplicationStoreContext.Provider>
+ );
+};
+
+export const useApplicationStore = (): ApplicationStore => {
+ const context = useContext(ApplicationStoreContext);
+ if (context == null || context.applicationStore == null) {
+ throw new Error("Requires application store provider!")
+ }
+ return context.applicationStore
+};
+
+export const useSelectApplicationState = <TProp extends unknown >( selector: (state: IApplicationStoreState) => TProp, eqFunc = (a: TProp, b: TProp) => a === b ): TProp => {
+ const context = useContext(ApplicationStoreContext);
+ if (context == null || context.applicationStore == null) {
+ throw new Error("Requires application store provider!")
+ }
+
+ const [propState, setPropState] = useState<TProp>(selector(context.applicationStore.state));
+
+ const selectorRef = useRef(selector);
+ selectorRef.current = selector;
+
+ const propStateRef = useRef({propState});
+ propStateRef.current.propState = propState;
+
+ useEffect(() => {
+ if (context == null || context.applicationStore == null) {
+ throw new Error("Requires application store provider!")
+ }
+
+ const changedHandler = () => {
+ const newState = selectorRef.current(context.applicationStore.state);
+ if (!eqFunc(newState, propStateRef.current.propState)) {
+ setPropState(newState);
+ }
+ };
+
+ if (LogLevel > 3) {
+ console.log("useSelectApplicationState: adding handler", changedHandler);
+ }
+
+ context.applicationStore.changed.addHandler(changedHandler);
+
+ return () => {
+ if (LogLevel > 3) {
+ console.log("useSelectApplicationState: removing handler", changedHandler);
+ }
+
+ context.applicationStore.changed.removeHandler(changedHandler);
+ }
+ }, [context]);
+
+ return propState;
+
+};
+
+export const useApplicationDispatch = (): Dispatch => {
+ const context = useContext(ApplicationStoreContext);
+ if (context == null || context.applicationStore == null) {
+ throw new Error("Requires application store provider!")
+ }
+ return context.applicationStore.dispatch;
+};