react-native-navigation
Version:
React Native Navigation - truly native navigation for iOS and Android
94 lines (80 loc) • 2.98 kB
text/typescript
import type { ComponentType } from 'react';
import merge from 'lodash/merge';
import isFunction from 'lodash/isFunction';
import { Store } from '../components/Store';
import { Options } from '../interfaces/Options';
import {
Layout,
LayoutBottomTabs,
LayoutComponent,
LayoutSideMenu,
LayoutSplitView,
LayoutStack,
LayoutTopTabs,
} from '../interfaces/Layout';
import { UniqueIdProvider } from 'src/adapters/UniqueIdProvider';
import { LayoutType } from './LayoutType';
type ComponentWithOptions = ComponentType<any> & { options(passProps: any): Options };
export class OptionsCrawler {
constructor(public readonly store: Store, public readonly uniqueIdProvider: UniqueIdProvider) {
this.crawl = this.crawl.bind(this);
}
crawl(api?: Layout<any>): void {
if (!api) return;
if (api.topTabs) {
this.topTabs(api.topTabs);
} else if (api.sideMenu) {
return this.sideMenu(api.sideMenu);
} else if (api.bottomTabs) {
return this.bottomTabs(api.bottomTabs);
} else if (api.stack) {
return this.stack(api.stack);
} else if (api.component) {
return this.component(api.component);
} else if (api.splitView) {
return this.splitView(api.splitView);
}
}
private topTabs(api: LayoutTopTabs): void {
api.children?.map(this.crawl);
}
private sideMenu(sideMenu: LayoutSideMenu): void {
this.crawl(sideMenu.center);
this.crawl(sideMenu.left);
this.crawl(sideMenu.right);
}
private bottomTabs(bottomTabs: LayoutBottomTabs): void {
bottomTabs.children?.map(this.crawl);
}
private stack(stack: LayoutStack): void {
stack.children?.map(this.crawl);
}
private splitView(splitView: LayoutSplitView): void {
splitView.detail && this.crawl(splitView.detail);
splitView.master && this.crawl(splitView.master);
}
private component(component: LayoutComponent): void {
this.applyComponentId(component);
this.applyStaticOptions(component);
}
private applyComponentId(component: LayoutComponent): void {
component.id = component.id || this.uniqueIdProvider.generate(LayoutType.Component);
}
private isComponentWithOptions(component: any): component is ComponentWithOptions {
return (component as ComponentWithOptions).options !== undefined;
}
private applyStaticOptions(layout: LayoutComponent) {
const staticOptions = this.staticOptionsIfPossible(layout);
layout.options = merge({}, staticOptions, layout.options);
}
private staticOptionsIfPossible(layout: LayoutComponent) {
const foundReactGenerator = this.store.getComponentClassForName(layout.name!);
const reactComponent = foundReactGenerator ? foundReactGenerator() : undefined;
if (reactComponent && this.isComponentWithOptions(reactComponent)) {
return isFunction(reactComponent.options)
? reactComponent.options({ componentId: layout.id, ...layout.passProps })
: reactComponent.options;
}
return {};
}
}