UNPKG

react-router-magic

Version:
169 lines (151 loc) 4.94 kB
import isFunction = require('lodash.isfunction'); import { RouterStoreState } from './Store'; import { MouseEvent } from 'react'; import { LocationDescriptorObject, History, Location } from 'history'; import isString = require('lodash.isstring'); export type Match = null | undefined | boolean | object; export type ValOrFunc<Result> = Result | ((location: Location, parentMatch: Match) => Result); export type NavParams = { href: string; match: Match; location: Location; navigate: () => void; handleAnchorClick: (event: MouseEvent<any>) => void; }; export type StringOrLocationDescriptorObject = string | LocationDescriptorObject; export type ToProps = ( StringOrLocationDescriptorObject | ((currentMatch: Match, location: Location, parentMatch: Match) => StringOrLocationDescriptorObject) ); export function execValOrFunc<Val>( valOrFunc: ValOrFunc<Val>, location: Location, parentMatch: Match, ): Val { if (isFunction(valOrFunc)) { return valOrFunc(location, parentMatch); } return valOrFunc; } export function checkSwitchState( props: { switchIndex?: number | false }, parentRouterState: RouterStoreState, match: Match, ): Match { if (parentRouterState.switch === false && props.switchIndex !== undefined) { console.warn(`You should not set switchIndex props yourself`); } if (parentRouterState.switch && props.switchIndex === undefined) { console.warn(`Error: Switch children are supposed to have switchIndex passed from parent`); } // in a switch but there are no match if ( parentRouterState.switch && parentRouterState.switch.matchIndex === false ) { return false; } // in a switch but switchIndex does not match if ( parentRouterState.switch && props.switchIndex !== undefined && parentRouterState.switch.matchIndex !== props.switchIndex ) { return false; } // ok match ! return match; } export function isModifiedEvent(event: MouseEvent<any>): boolean { return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); } export function createNavParams( to: ToProps, history: History, location: Location, replace: boolean = false, match?: ValOrFunc<Match>, parentMatch?: Match, ): NavParams { const doMatch: Match = match ? execValOrFunc(match, location, parentMatch) : null; const toLocation: LocationDescriptorObject = execTo(to, doMatch, location, parentMatch); const href: string = history.createHref(toLocation); const doNavigate: () => void = () => navigate(toLocation, history, replace); const params: NavParams = { href: href, navigate: doNavigate, match: doMatch, location: location, handleAnchorClick: createHandleAnchorClick(doNavigate), }; return params; } export function createHandleAnchorClick(navigate: () => void): (event: MouseEvent<any>, target?: string) => void { return (event: MouseEvent<any>, target?: string) => { if ( !event.defaultPrevented && // onClick prevented default event.button === 0 && // ignore right clicks !target && // let browser handle "target=_blank" etc. !isModifiedEvent(event) // ignore clicks with modifier keys ) { event.preventDefault(); navigate(); } }; } export function navigate(to: LocationDescriptorObject, history: History, replace: boolean = false): void { if (replace) { history.replace(to); } else { history.push(to); } } export function execTo( to: ToProps, match: Match, location: Location, parentMatch?: Match, ): LocationDescriptorObject { const toExecuted: StringOrLocationDescriptorObject = isFunction(to) ? ( to(match, location, parentMatch) ) : to; return isString(toExecuted) ? { pathname: toExecuted } : toExecuted; } const REACT_STATICS: { [key: string]: boolean } = { childContextTypes: true, contextTypes: true, defaultProps: true, displayName: true, getDefaultProps: true, mixins: true, propTypes: true, type: true, }; const KNOWN_STATICS: {[key: string]: boolean} = { name: true, length: true, prototype: true, caller: true, arguments: true, arity: true, }; export function hoistNonReactStatics( targetComponent: any, sourceComponent: any, customStatics?: { [key: string]: boolean }, ) { if (typeof sourceComponent !== 'string') { // don't hoist over string (html) components let keys: string[] = Object.getOwnPropertyNames(sourceComponent); for (let i = 0; i < keys.length; ++i) { if (!REACT_STATICS[keys[i]] && !KNOWN_STATICS[keys[i]] && (!customStatics || !customStatics[keys[i]])) { try { targetComponent[keys[i]] = sourceComponent[keys[i]]; } catch (error) { console.warn(error); } } } } return targetComponent; }