Skip to content

Commit

Permalink
Merge pull request #53 from absurdprofit/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
absurdprofit authored Aug 3, 2024
2 parents c42ac88 + 8ce941d commit 1c021e3
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 53 deletions.
14 changes: 8 additions & 6 deletions packages/core/src/RouterBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ import {
import { NestedRouterContext, RouterContext } from './RouterContext';
import { dispatchEvent, matchRoute, resolveBaseURLFromPattern } from './common/utils';
import { Component, createRef, isValidElement, Children } from 'react';
import { ScreenBase } from './ScreenBase';
import { ScreenBase, ScreenBaseConfig } from './ScreenBase';
import { LoadEvent } from './common/events';

export interface RouterBaseConfig {
screenConfig?: ScreenBaseConfig;
basePath?: string;
}

export interface RouterBaseProps<S extends ScreenBase = ScreenBase> {
id?: string;
config: {
screenConfig?: S["props"]["config"];
basePath?: string;
};
config?: RouterBaseConfig;
children: ScreenChild<S> | ScreenChild<S>[];
}

Expand Down Expand Up @@ -169,7 +171,7 @@ export abstract class RouterBase<P extends RouterBaseProps = RouterBaseProps, S
get baseURLPattern() {
let baseURL = window.location.origin + "/";
const defaultBasePathname = this.isRoot ? new URL(".", document.baseURI).href.replace(baseURL, '') : ".";
let basePathname = this.props.config.basePath ?? defaultBasePathname;
let basePathname = this.props.config?.basePath ?? defaultBasePathname;

if (this.parent && this.parentScreen) {
const { resolvedPathname = window.location.pathname, path } = this.parentScreen;
Expand Down
44 changes: 25 additions & 19 deletions packages/core/src/ScreenBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,48 @@ export interface ScreenBaseComponentProps<
navigation: N;
}

export interface LifecycleProps<R extends RoutePropBase> extends ScreenBaseComponentProps<R, NavigationBase> {
export interface LifecycleProps<R extends RoutePropBase, N extends NavigationBase = NavigationBase> extends ScreenBaseComponentProps<R, N> {
signal: AbortSignal;
}

export interface ScreenBaseProps<R extends RoutePropBase = RoutePropBase> {
export interface ScreenBaseConfig<R extends RoutePropBase = RoutePropBase, N extends NavigationBase = NavigationBase> {
header?: {
fallback?: React.ReactNode;
component: React.JSXElementConstructor<any> | LazyExoticComponent<any>
};
footer?: {
fallback?: React.ReactNode;
component: React.JSXElementConstructor<any> | LazyExoticComponent<any>
};
animation?: AnimationEffectFactory;
onEnter?: (props: LifecycleProps<R, N>) => void | Promise<void>;
onExit?: (props: LifecycleProps<R, N>) => void | Promise<void>;
onEntered?: (props: LifecycleProps<R, N>) => void | Promise<void>;
onExited?: (props: LifecycleProps<R, N>) => void | Promise<void>;
onLoad?: (props: LifecycleProps<R, N>) => void | Promise<void>;
}

export interface ScreenBaseProps {
component: React.JSXElementConstructor<any> | LazyExoticComponent<any>;
fallback?: React.ReactNode;
path: string;
resolvedPathname?: string;
defaultParams?: PlainObject;
caseSensitive?: boolean;
id?: string;
config?: {
header?: {
fallback?: React.ReactNode;
component: React.JSXElementConstructor<any> | LazyExoticComponent<any>
};
footer?: {
fallback?: React.ReactNode;
component: React.JSXElementConstructor<any> | LazyExoticComponent<any>
};
animation?: AnimationEffectFactory;
onEnter?: (props: LifecycleProps<R>) => void | Promise<void>;
onExit?: (props: LifecycleProps<R>) => void | Promise<void>;
onEntered?: (props: LifecycleProps<R>) => void | Promise<void>;
onExited?: (props: LifecycleProps<R>) => void | Promise<void>;
onLoad?: (props: LifecycleProps<R>) => void | Promise<void>;
}
config?: ScreenBaseConfig;
}

export interface ScreenBaseState {
focused: boolean;
elementType: ElementType;
}

export abstract class ScreenBase<P extends ScreenBaseProps = ScreenBaseProps, S extends ScreenBaseState = ScreenBaseState, R extends RoutePropBase<ScreenBaseProps["config"]> = RoutePropBase<ScreenBaseProps["config"]>> extends Component<P, S> {
export abstract class ScreenBase<
P extends ScreenBaseProps = ScreenBaseProps,
S extends ScreenBaseState = ScreenBaseState,
R extends RoutePropBase<P["config"]> = RoutePropBase<P["config"]>
> extends Component<P, S> {
public readonly sharedElementScene: SharedElementScene;
#transitionProvider = createRef<ScreenTransitionProvider>();
protected readonly ref = createRef<HTMLDivElement>();
Expand Down
34 changes: 18 additions & 16 deletions packages/stack/src/Router.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import { RouterBase, includesRoute, isValidScreenChild, matchRoute } from '@react-motion-router/core';
import type { LoadEvent, NestedRouterContext, PlainObject, RouterBaseProps, RouterBaseState, ScreenChild } from '@react-motion-router/core';
import type { LoadEvent, NestedRouterContext, PlainObject, RouterBaseConfig, RouterBaseProps, RouterBaseState, ScreenChild } from '@react-motion-router/core';
import { Navigation } from './Navigation';
import { ScreenProps, Screen } from './Screen';
import { HistoryEntryState, isHorizontalDirection, isRefObject, SwipeDirection } from './common/types';
import { ScreenProps, Screen, ScreenConfig } from './Screen';
import { HistoryEntryState, isHorizontalDirection, isRefObject, StackRouterEventMap, SwipeDirection } from './common/types';
import { Children, createRef, cloneElement, startTransition } from 'react';
import { SwipeStartEvent, SwipeEndEvent } from 'web-gesture-events';
import { GestureTimeline } from 'web-animations-extension';
import { deepEquals, isRollback, searchParamsToObject } from './common/utils';
import { GestureCancelEvent, GestureEndEvent, GestureStartEvent } from './common/events';
import { DEFAULT_GESTURE_CONFIG } from './common/constants';

export interface RouterConfig extends RouterBaseConfig {
screenConfig?: ScreenConfig;
disableBrowserRouting?: boolean;
initialPath?: string;
shouldIntercept?(navigateEvent: NavigateEvent): boolean;
onIntercept?(navigateEvent: NavigateEvent): boolean;
}

export interface RouterProps extends RouterBaseProps<Screen> {
config: RouterBaseProps["config"] & {
screenConfig?: ScreenProps["config"];
disableBrowserRouting?: boolean;
initialPath?: string;
shouldIntercept?(navigateEvent: NavigateEvent): boolean;
onIntercept?(navigateEvent: NavigateEvent): boolean;
}
config?: RouterConfig;
}

export interface RouterState extends RouterBaseState {
Expand All @@ -34,7 +36,7 @@ export interface RouterState extends RouterBaseState {
documentTitle?: string;
}

export class Router extends RouterBase<RouterProps, RouterState> {
export class Router extends RouterBase<RouterProps, RouterState, StackRouterEventMap> {
public readonly navigation = new Navigation(this);

constructor(props: RouterProps, context: React.ContextType<typeof NestedRouterContext>) {
Expand Down Expand Up @@ -199,12 +201,12 @@ export class Router extends RouterBase<RouterProps, RouterState> {
);
});

if (!isValidScreenChild(screenChild)) return null;
if (!isValidScreenChild<Screen>(screenChild)) return null;

return cloneElement(screenChild, {
config: {
title: document.title,
...this.props.config.screenConfig,
...this.props.config?.screenConfig,
...screenChild.props.config,
...config
},
Expand Down Expand Up @@ -246,7 +248,7 @@ export class Router extends RouterBase<RouterProps, RouterState> {
}

protected shouldIntercept(e: NavigateEvent): boolean {
if (this.props.config.shouldIntercept)
if (this.props.config?.shouldIntercept)
return this.props.config.shouldIntercept(e);
return e.canIntercept
&& !e.formData
Expand All @@ -255,7 +257,7 @@ export class Router extends RouterBase<RouterProps, RouterState> {
}

protected intercept(e: NavigateEvent | LoadEvent): void {
if (this.props.config.onIntercept && e.navigationType !== "load")
if (this.props.config?.onIntercept && e.navigationType !== "load")
if (this.props.config.onIntercept(e) || e.defaultPrevented)
return;

Expand Down Expand Up @@ -293,7 +295,7 @@ export class Router extends RouterBase<RouterProps, RouterState> {

return new Promise<void>((resolve, reject) => startTransition(() => {
this.setState({ screenStack, fromKey, transition, destinationKey }, async () => {
const { initialPath } = this.props.config;
const { initialPath } = this.props.config ?? {};
const [firstEntry] = entries;
if (
initialPath
Expand Down
26 changes: 14 additions & 12 deletions packages/stack/src/Screen.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
import { ScreenBase } from '@react-motion-router/core';
import type { PlainObject, RouterContext, ScreenBaseProps, ScreenBaseState, ScreenBaseComponentProps } from '@react-motion-router/core';
import type { PlainObject, RouterContext, ScreenBaseProps, ScreenBaseState, ScreenBaseComponentProps, ScreenBaseConfig } from '@react-motion-router/core';
import { Navigation } from './Navigation';
import { RouteProp, SwipeDirection } from './common/types';
import { Router } from './Router';

export interface ScreenComponentProps<T extends PlainObject = {}> extends ScreenBaseComponentProps<RouteProp<T>, Navigation> { }

export interface ScreenConfig extends ScreenBaseConfig<RouteProp> {
title?: string;
presentation?: "default" | "dialog" | "modal";
keepAlive?: boolean;
gestureDirection?: SwipeDirection;
gestureAreaWidth?: number;
gestureMinFlingVelocity?: number;
gestureHysteresis?: number;
disableGesture?: boolean;
}

export interface ScreenProps extends ScreenBaseProps {
config?: ScreenBaseProps["config"] & {
title?: string;
presentation?: "default" | "dialog" | "modal";
keepAlive?: boolean;
gestureDirection?: SwipeDirection;
gestureAreaWidth?: number;
gestureMinFlingVelocity?: number;
gestureHysteresis?: number;
disableGesture?: boolean;
}
config?: ScreenConfig;
}

export interface ScreenState extends ScreenBaseState { }

export class Screen extends ScreenBase<ScreenProps, ScreenState> {
export class Screen extends ScreenBase<ScreenProps, ScreenState, RouteProp> {
readonly routeProp;

constructor(props: ScreenProps, context: React.ContextType<typeof RouterContext>) {
Expand Down

0 comments on commit 1c021e3

Please sign in to comment.