Skip to content

Commit

Permalink
refactor(sanity): update router api and exports
Browse files Browse the repository at this point in the history
  • Loading branch information
mariuslundgard authored and rexxars committed Sep 14, 2022
1 parent 8b8f3d6 commit bfedd3c
Show file tree
Hide file tree
Showing 16 changed files with 78 additions and 85 deletions.
9 changes: 7 additions & 2 deletions packages/sanity/src/router/IntentLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ export const IntentLink = forwardRef(function IntentLink(
ref: ForwardedRef<HTMLAnchorElement>
) {
const {intent, params, target, ...restProps} = props
const {handleClick, href} = useIntentLink({intent, params, target, onClick: props.onClick})
const {onClick, href} = useIntentLink({
intent,
params,
target,
onClick: props.onClick,
})

return <a {...restProps} href={href} onClick={handleClick} ref={ref} />
return <a {...restProps} href={href} onClick={onClick} ref={ref} target={target} />
})
6 changes: 3 additions & 3 deletions packages/sanity/src/router/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export const Link = forwardRef(function Link(
props: LinkProps & React.HTMLProps<HTMLAnchorElement>,
ref: React.ForwardedRef<HTMLAnchorElement>
) {
const {onClick, href, target, replace, ...restProps} = props
const {handleClick} = useLink({onClick, href, target, replace})
const {onClick: onClickProp, href, target, replace, ...restProps} = props
const {onClick} = useLink({onClick: onClickProp, href, target, replace})

return <a {...restProps} onClick={handleClick} href={href} target={target} ref={ref} />
return <a {...restProps} onClick={onClick} href={href} target={target} ref={ref} />
})
2 changes: 1 addition & 1 deletion packages/sanity/src/router/RouterContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import React from 'react'
import {RouterContextValue} from './types'

/**
* @public
* @internal
*/
export const RouterContext = React.createContext<RouterContextValue | null>(null)
12 changes: 9 additions & 3 deletions packages/sanity/src/router/StateLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ export const StateLink = forwardRef(function StateLink(
props: StateLinkProps & Omit<React.HTMLProps<HTMLAnchorElement>, 'href'>,
ref: React.ForwardedRef<HTMLAnchorElement>
) {
const {onClick, replace, state, target, toIndex = false, ...restProps} = props
const {handleClick, href} = useStateLink({onClick, replace, state, target, toIndex})
const {onClick: onClickProp, replace, state, target, toIndex = false, ...restProps} = props
const {onClick, href} = useStateLink({
onClick: onClickProp,
replace,
state,
target,
toIndex,
})

return <a {...restProps} href={href} onClick={handleClick} ref={ref} />
return <a {...restProps} href={href} onClick={onClick} ref={ref} />
})
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import {difference, intersection, isPlainObject, pick} from 'lodash'
import {difference, intersection, pick} from 'lodash'
import {isRecord} from '../util'
import {RouterNode, MatchResult} from './types'
import {arrayify} from './utils/arrayify'

function isRecord(value: unknown): value is Record<string, unknown> {
return isPlainObject(value)
}

function createMatchResult(
nodes: RouterNode[],
missing: string[],
Expand All @@ -14,7 +11,8 @@ function createMatchResult(
return {nodes, missing, remaining}
}

export function findMatchingRoutes(
/** @internal */
export function _findMatchingRoutes(
node: RouterNode,
_state?: Record<string, unknown>
): MatchResult {
Expand Down Expand Up @@ -57,7 +55,7 @@ export function findMatchingRoutes(
let matchingChild: MatchResult = {nodes: [], remaining: [], missing: []}

arrayify(children).some((childNode) => {
matchingChild = findMatchingRoutes(childNode, remainingState)
matchingChild = _findMatchingRoutes(childNode, remainingState)
return matchingChild.nodes.length > 0
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ function createSegment(segment: string): RouteSegment | null {
return {type: 'dir', name: segment}
}

export function parseRoute(route: string): Route {
/** @internal */
export function _parseRoute(route: string): Route {
const [pathname] = route.split('?')

const segments = pathname.split('/').map(createSegment).filter(Boolean) as RouteSegment[]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import {flatten} from 'lodash'
import {findMatchingRoutes} from './findMatchingRoutes'
import {_findMatchingRoutes} from './_findMatchingRoutes'
import {RouterNode, MatchResult} from './types'
import {debug} from './utils/debug'

/**
* @public
*/
export function resolvePathFromState(node: RouterNode, state: Record<string, unknown>): string {
/** @internal */
export function _resolvePathFromState(node: RouterNode, state: Record<string, unknown>): string {
debug('Resolving path from state %o', state)

const match: MatchResult = findMatchingRoutes(node, state)
const match: MatchResult = _findMatchingRoutes(node, state)

if (match.remaining.length > 0) {
const remaining = match.remaining
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ function matchPath(node: RouterNode, path: string): Record<string, string> | nul
}

/**
* @public
* @internal
*/
export function resolveStateFromPath(node: RouterNode, path: string): Record<string, any> | null {
export function _resolveStateFromPath(node: RouterNode, path: string): Record<string, any> | null {
debug('resolving state from path %s', path)

const pathMatch = matchPath(node, path.split('?')[0])
Expand Down
38 changes: 19 additions & 19 deletions packages/sanity/src/router/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {parseRoute} from './parseRoute'
import {resolveStateFromPath} from './resolveStateFromPath'
import {resolvePathFromState} from './resolvePathFromState'
import {_parseRoute} from './_parseRoute'
import {_resolveStateFromPath} from './_resolveStateFromPath'
import {_resolvePathFromState} from './_resolvePathFromState'
import {RouteTransform, Router, RouteChildren} from './types'
import {decodeJsonParams, encodeJsonParams} from './utils/jsonParamsEncoding'
import {decodeParams, encodeParams} from './utils/paramsEncoding'
Expand All @@ -17,6 +17,19 @@ export type NodeOptions = {
scope?: string
}

/**
* @public
*/
export const route: {
create: (
routeOrOpts: NodeOptions | string,
childrenOrOpts?: NodeOptions | RouteChildren | null,
children?: Router | RouteChildren
) => Router
intents: (base: string) => Router
scope: (scopeName: string, ...rest: any[]) => Router
} = {create: createRoute, scope: routeScope, intents: routeIntents}

function normalizeChildren(children: any): RouteChildren {
if (Array.isArray(children) || typeof children === 'function') {
return children
Expand Down Expand Up @@ -53,19 +66,6 @@ function normalizeArgs(
return {path, ...childrenOrOpts}
}

/**
* @public
*/
export const route: {
create: (
routeOrOpts: NodeOptions | string,
childrenOrOpts?: NodeOptions | RouteChildren | null,
children?: Router | RouteChildren
) => Router
intents: (base: string) => Router
scope: (scopeName: string, ...rest: any[]) => Router
} = {create: createRoute, scope: routeScope, intents: routeIntents}

function createRoute(
routeOrOpts: NodeOptions | string,
childrenOrOpts?: NodeOptions | RouteChildren | null,
Expand Down Expand Up @@ -129,7 +129,7 @@ function createNode(options: NodeOptions): Router {
throw new TypeError('Missing path')
}

const parsedRoute = parseRoute(path)
const parsedRoute = _parseRoute(path)

return {
_isRoute: true, // todo: make a Router class instead
Expand All @@ -138,10 +138,10 @@ function createNode(options: NodeOptions): Router {
children: children || [],
transform,
encode(state) {
return resolvePathFromState(this, state)
return _resolvePathFromState(this, state)
},
decode(_path) {
return resolveStateFromPath(this, _path)
return _resolveStateFromPath(this, _path)
},
isRoot: isRoot,
isNotFound(pathname: string): boolean {
Expand Down
1 change: 1 addition & 0 deletions packages/sanity/src/router/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export interface MatchResult {
missing: string[]
remaining: string[]
}

/**
* @public
*/
Expand Down
12 changes: 6 additions & 6 deletions packages/sanity/src/router/useIntentLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import {useRouter} from './useRouter'
* ```tsx
* import {useIntentLink} from 'sanity'
*
* const {handleClick, href} = useIntentLink({
* const {onClick, href} = useIntentLink({
* intent: 'edit',
* params: {id: 'foo'}
* })
*
* <a href={href} onClick={handleClick}>Link to "foo" editor</a>
* <a href={href} onClick={onClick}>Link to "foo" editor</a>
* ```
*
* @public
Expand All @@ -25,13 +25,13 @@ export function useIntentLink(props: {
replace?: boolean
target?: string
}): {
handleClick: React.MouseEventHandler<HTMLElement>
onClick: React.MouseEventHandler<HTMLElement>
href: string
} {
const {intent, onClick, params, replace, target} = props
const {intent, onClick: onClickProp, params, replace, target} = props
const {resolveIntentLink} = useRouter()
const href = resolveIntentLink(intent, params)
const {handleClick} = useLink({href, onClick, replace, target})
const {onClick} = useLink({href, onClick: onClickProp, replace, target})

return {handleClick, href}
return {onClick, href}
}
14 changes: 7 additions & 7 deletions packages/sanity/src/router/useLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ export function useLink(props: {
onClick?: React.MouseEventHandler<HTMLElement>
replace?: boolean
target?: string
}): {handleClick: React.MouseEventHandler<HTMLElement>} {
const {onClick, href, target, replace = false} = props
}): {onClick: React.MouseEventHandler<HTMLElement>} {
const {onClick: onClickProp, href, target, replace = false} = props
const {navigateUrl} = useRouter()

const handleClick = useCallback(
const onClick = useCallback(
(event: React.MouseEvent<HTMLElement>): void => {
if (event.isDefaultPrevented()) {
return
}

if (!href) return

if (onClick) {
onClick(event)
if (onClickProp) {
onClickProp(event)
}

if (isModifiedEvent(event) || !isLeftClickEvent(event)) {
Expand All @@ -46,8 +46,8 @@ export function useLink(props: {

navigateUrl({path: href, replace})
},
[href, navigateUrl, onClick, replace, target]
[href, navigateUrl, onClickProp, replace, target]
)

return {handleClick}
return {onClick: onClick}
}
20 changes: 2 additions & 18 deletions packages/sanity/src/router/useRouterState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import {useRouter} from './useRouter'
* @public
*/
export function useRouterState<R = RouterState>(selector: (routerState: RouterState) => R): R

/**
* @public
*/
export function useRouterState(): RouterState

/**
* @public
*/
Expand All @@ -23,23 +25,5 @@ export function useRouterState(
// reset the state when the `selector` prop changes
useEffect(() => setState(selector(state)), [selector, state])

// TODO
// update the state via a subscription
// useEffect(() => {
// // prevents "Can't perform a React state update on an unmounted component."
// const mounted = {current: true}

// const unsubscribe = channel.subscribe(() => {
// if (mounted.current) {
// setState(selector(state))
// }
// })

// return () => {
// mounted.current = false
// unsubscribe()
// }
// }, [channel, selector, getState])

return selectedState
}
8 changes: 4 additions & 4 deletions packages/sanity/src/router/useStateLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ export function useStateLink(props: {
target?: string
toIndex?: boolean
}): {
handleClick: React.MouseEventHandler<HTMLElement>
onClick: React.MouseEventHandler<HTMLElement>
href: string
} {
const {onClick, replace, state, target, toIndex = false} = props
const {onClick: onClickProp, replace, state, target, toIndex = false} = props

if (state && toIndex) {
throw new Error('Passing both `state` and `toIndex={true}` as props to StateLink is invalid')
Expand All @@ -39,7 +39,7 @@ export function useStateLink(props: {
[resolvePathFromState, state, toIndex]
)

const {handleClick} = useLink({href, onClick, replace, target})
const {onClick} = useLink({href, onClick: onClickProp, replace, target})

return {handleClick, href}
return {onClick, href}
}
4 changes: 2 additions & 2 deletions packages/sanity/src/router/withRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {ComponentType} from 'react'
import React, {ComponentType, FunctionComponent} from 'react'
import {RouterContextValue} from './types'
import {useRouter} from './useRouter'

Expand All @@ -7,7 +7,7 @@ import {useRouter} from './useRouter'
*/
export function withRouter<Props extends {router: RouterContextValue}>(
Component: ComponentType<Props>
) {
): FunctionComponent<Omit<Props, 'router'>> {
function WithRouter(props: Omit<Props, 'router'>) {
const router = useRouter()

Expand Down
8 changes: 4 additions & 4 deletions packages/sanity/src/studio/components/navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function Navbar(props: NavbarProps) {
const routerState = useRouterState()
const ToolMenu = navbar?.components?.ToolMenu || DefaultToolMenu
const {scheme} = useColorScheme()
const rootLink = useStateLink({state: {}})
const {href: rootHref, onClick: handleRootClick} = useStateLink({state: {}})
const mediaIndex = useMediaIndex()
const activeToolName = typeof routerState.tool === 'string' ? routerState.tool : undefined

Expand Down Expand Up @@ -161,15 +161,15 @@ export function Navbar(props: NavbarProps) {
<Button
aria-label={title}
as="a"
href={rootLink.href}
href={rootHref}
mode="bleed"
onClick={rootLink.handleClick}
onClick={handleRootClick}
padding={3}
>
{rootLinkContent}
</Button>
),
[rootLink.handleClick, rootLink.href, rootLinkContent, title]
[handleRootClick, rootHref, rootLinkContent, title]
)

// The HTML elements that are part of the search view (i.e. the "close" button that is visible
Expand Down

0 comments on commit bfedd3c

Please sign in to comment.