Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions packages/core/src/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ export const stackTrace = (autolog = true) => {
try {
throw new Error()
} catch (e) {
// @ts-ignore
const stack = e.stack
const stack = (e as Error).stack

if (!autolog) {
return stack
Expand Down
13 changes: 4 additions & 9 deletions packages/core/src/head.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import debounce from './debounce'
import { HeadManager, HeadOnUpdateCallback, HeadTitleCallback } from './types'

const Renderer = {
buildDOMElement(tag: string): ChildNode {
Expand Down Expand Up @@ -57,15 +58,9 @@ const Renderer = {

export default function createHeadManager(
isServer: boolean,
titleCallback: (title: string) => string,
onUpdate: (elements: string[]) => void,
): {
forceUpdate: () => void
createProvider: () => {
update: (elements: string[]) => void
disconnect: () => void
}
} {
titleCallback: HeadTitleCallback,
onUpdate: HeadOnUpdateCallback,
): HeadManager {
const states: Record<string, Array<string>> = {}
let lastProviderId = 0

Expand Down
21 changes: 8 additions & 13 deletions packages/core/src/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,7 @@ import { eventHandler } from './eventHandler'
import { fireNavigateEvent } from './events'
import { history } from './history'
import { Scroll } from './scroll'
import {
Component,
Page,
PageEvent,
PageHandler,
PageResolver,
PreserveStateOption,
RouterInitParams,
VisitOptions,
} from './types'
import { Component, Page, PageEvent, PageHandler, PageResolver, RouterInitParams } from './types'
import { hrefToUrl, isSameUrlWithoutHash } from './url'

class CurrentPage {
Expand Down Expand Up @@ -41,7 +32,11 @@ class CurrentPage {
replace = false,
preserveScroll = false,
preserveState = false,
}: Partial<Pick<VisitOptions, 'replace' | 'preserveScroll' | 'preserveState'>> = {},
}: {
replace?: boolean
preserveScroll?: boolean
preserveState?: boolean
} = {},
): Promise<void> {
if (Object.keys(page.deferredProps || {}).length) {
this.pendingDeferredProps = {
Expand Down Expand Up @@ -123,7 +118,7 @@ class CurrentPage {
{
preserveState = false,
}: {
preserveState?: PreserveStateOption
preserveState?: boolean
} = {},
) {
return this.resolve(page.component).then((component) => {
Expand Down Expand Up @@ -167,7 +162,7 @@ class CurrentPage {
}: {
component: Component
page: Page
preserveState: PreserveStateOption
preserveState: boolean
}): Promise<unknown> {
return this.swapComponent({ component, page, preserveState })
}
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/requestParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ export class RequestParams {
}

public setPreserveOptions(page: Page) {
this.params.preserveScroll = this.resolvePreserveOption(this.params.preserveScroll, page)
this.params.preserveState = this.resolvePreserveOption(this.params.preserveState, page)
this.params.preserveScroll = RequestParams.resolvePreserveOption(this.params.preserveScroll, page)
this.params.preserveState = RequestParams.resolvePreserveOption(this.params.preserveState, page)
}

public runCallbacks() {
Expand Down Expand Up @@ -169,7 +169,7 @@ export class RequestParams {
this.callbacks.push({ name, args })
}

protected resolvePreserveOption(value: PreserveStateOption, page: Page): boolean {
public static resolvePreserveOption(value: PreserveStateOption, page: Page): boolean {
if (typeof value === 'function') {
return value(page)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ export class Response {

return currentPage.set(pageResponse, {
replace: this.requestParams.all().replace,
preserveScroll: this.requestParams.all().preserveScroll,
preserveState: this.requestParams.all().preserveState,
preserveScroll: this.requestParams.all().preserveScroll as boolean,
preserveState: this.requestParams.all().preserveState as boolean,
})
}

Expand Down
27 changes: 15 additions & 12 deletions packages/core/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { page as currentPage } from './page'
import { polls } from './polls'
import { prefetchedRequests } from './prefetched'
import { Request } from './request'
import { RequestParams } from './requestParams'
import { RequestStream } from './requestStream'
import { Scroll } from './scroll'
import {
Expand Down Expand Up @@ -386,19 +387,21 @@ export class Router {

const { onError, onFinish, onSuccess, ...pageParams } = params

const page = {
...current,
...pageParams,
props: props as Page['props'],
}

const preserveScroll = RequestParams.resolvePreserveOption(params.preserveScroll ?? false, page)
const preserveState = RequestParams.resolvePreserveOption(params.preserveState ?? false, page)

currentPage
.set(
{
...current,
...pageParams,
props: props as Page['props'],
},
{
replace,
preserveScroll: params.preserveScroll,
preserveState: params.preserveState,
},
)
.set(page, {
replace,
preserveScroll,
preserveState,
})
.then(() => {
const errors = currentPage.get().props.errors || {}

Expand Down
34 changes: 29 additions & 5 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export type PageHandler = ({
}: {
component: Component
page: Page
preserveState: PreserveStateOption
preserveState: boolean
}) => Promise<unknown>

export type PreserveStateOption = boolean | 'errors' | ((page: Page) => boolean)
Expand Down Expand Up @@ -376,7 +376,32 @@ export type InternalActiveVisit = ActiveVisit & {
export type VisitId = unknown
export type Component = unknown

export type InertiaAppResponse = Promise<{ head: string[]; body: string } | void>
export type HeadTitleCallback = (title: string) => string
export type HeadOnUpdateCallback = (elements: string[]) => void
export type HeadManager = {
forceUpdate: () => void
createProvider: () => {
reconnect: () => void
update: HeadOnUpdateCallback
disconnect: () => void
}
}

export interface InertiaAppProgressOptions {
delay?: number
color?: string
includeCSS?: boolean
showSpinner?: boolean
}

export type InertiaAppSSRContent = { head: string[]; body: string }
export type InertiaAppResponse = Promise<InertiaAppSSRContent | void>

export type CreateInertiaAppOptions = {
id?: string
progress?: InertiaAppProgressOptions | false
resolve: PageResolver
}

export type LinkPrefetchOption = 'mount' | 'hover' | 'click'

Expand All @@ -403,9 +428,8 @@ export interface LinkComponentBaseProps
| 'queryStringArrayFormat'
| 'async'
> &
Omit<VisitCallbacks, 'onCancelToken'> & {
VisitCallbacks & {
href: string | UrlMethodPair
onCancelToken: (cancelToken: import('axios').CancelTokenSource) => void
prefetch: boolean | LinkPrefetchOption | LinkPrefetchOption[]
cacheFor: CacheForOption | CacheForOption[]
cacheTags: string | string[]
Expand Down Expand Up @@ -579,7 +603,7 @@ export interface InfiniteScrollRef {
}

export interface InfiniteScrollComponentBaseProps {
data?: string
data: string
buffer?: number
as?: string
manual?: boolean
Expand Down
43 changes: 28 additions & 15 deletions packages/react/src/App.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
import { createHeadManager, PageHandler, router } from '@inertiajs/core'
import { createElement, useEffect, useMemo, useState } from 'react'
import { createHeadManager, Page, PageHandler, PageProps, router } from '@inertiajs/core'
import { ComponentType, createElement, ReactNode, useEffect, useMemo, useState } from 'react'
import HeadContext from './HeadContext'
import PageContext from './PageContext'
import { AppComponent, AppOptions, RenderChildrenOptions } from './createInertiaApp'

type ReactPageHandler = (options: { component: ReactNode; page: Page; preserveState: boolean }) => Promise<void>

type CurrentPage = {
component: ReactNode
page: Page
key: number | null
}

let currentIsInitialPage = true
let routerIsInitialized = false
let swapComponent: PageHandler = async () => {
let swapComponent: ReactPageHandler = async () => {
// Dummy function so we can init the router outside of the useEffect hook. This is
// needed so `router.reload()` works right away (on mount) in any of the user's
// components. We swap in the real function in the useEffect hook below.
currentIsInitialPage = false
}

export default function App({
function App<SharedProps extends PageProps = PageProps>({
children,
initialPage,
initialComponent,
resolveComponent,
titleCallback,
onHeadUpdate,
}) {
const [current, setCurrent] = useState({
}: AppOptions<SharedProps>): ReturnType<AppComponent<SharedProps>> {
const [current, setCurrent] = useState<CurrentPage>({
component: initialComponent || null,
page: initialPage,
key: null,
Expand All @@ -38,7 +47,7 @@ export default function App({
router.init({
initialPage,
resolveComponent,
swapComponent: async (args) => swapComponent(args),
swapComponent: ((...args: Parameters<ReactPageHandler>) => swapComponent(...args)) as PageHandler,
})

routerIsInitialized = true
Expand Down Expand Up @@ -67,24 +76,26 @@ export default function App({
return createElement(
HeadContext.Provider,
{ value: headManager },
createElement(PageContext.Provider, { value: current.page }, null),
createElement(PageContext.Provider, { value: current.page }),
)
}

const renderChildren =
children ||
(({ Component, props, key }) => {
(({ Component, props, key }: RenderChildrenOptions<SharedProps>) => {
const child = createElement(Component, { key, ...props })

if (typeof Component.layout === 'function') {
return Component.layout(child)
}

if (Array.isArray(Component.layout)) {
return Component.layout
.concat(child)
return [...Component.layout]
.reverse()
.reduce((children, Layout) => createElement(Layout, { children, ...props }))
.reduce(
(children: ReactNode, Layout: ComponentType): ReactNode => createElement(Layout, { children, ...props }),
child,
)
}

return child
Expand All @@ -97,12 +108,14 @@ export default function App({
PageContext.Provider,
{ value: current.page },
renderChildren({
Component: current.component,
Component: current.component as unknown as ComponentType,
key: current.key,
props: current.page.props,
}),
props: current.page.props as Page<SharedProps>['props'],
}) as ReactNode,
),
)
}

App.displayName = 'Inertia'

export default App
6 changes: 3 additions & 3 deletions packages/react/src/Form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const Form = forwardRef<FormComponentRef, ComponentProps>(
const [isDirty, setIsDirty] = useState(false)
const defaultData = useRef<FormData>(new FormData())

const getFormData = (): FormData => new FormData(formElement.current)
const getFormData = (): FormData => new FormData(formElement.current!)

// Convert the FormData to an object because we can't compare two FormData
// instances directly (which is needed for isDirty), mergeDataIntoQueryString()
Expand All @@ -96,13 +96,13 @@ const Form = forwardRef<FormComponentRef, ComponentProps>(

const formEvents: Array<keyof HTMLElementEventMap> = ['input', 'change', 'reset']

formEvents.forEach((e) => formElement.current.addEventListener(e, updateDirtyState))
formEvents.forEach((e) => formElement.current!.addEventListener(e, updateDirtyState))

return () => formEvents.forEach((e) => formElement.current?.removeEventListener(e, updateDirtyState))
}, [])

const reset = (...fields: string[]) => {
resetFormFields(formElement.current, defaultData.current, fields)
resetFormFields(formElement.current!, defaultData.current, fields)
}

const resetAndClearErrors = (...fields: string[]) => {
Expand Down
Loading