Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: inertiajs/inertia
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: skore/inertia
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.
  • 17 commits
  • 4 files changed
  • 3 contributors

Commits on Apr 23, 2021

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    4771684 View commit details
  2. Copy the full SHA
    8c15cf9 View commit details
  3. Formatting

    reinink committed Apr 23, 2021
    Copy the full SHA
    18a30be View commit details
  4. Add "appear" prop to dialogs

    reinink committed Apr 23, 2021
    Copy the full SHA
    04f5a4d View commit details

Commits on May 7, 2021

  1. Copy the full SHA
    de115ac View commit details

Commits on May 23, 2021

  1. Attempt to fix dialogs

    claudiodekker committed May 23, 2021
    Copy the full SHA
    2cf93ca View commit details
  2. Fix transformProps

    claudiodekker committed May 23, 2021
    Copy the full SHA
    cd1747d View commit details
  3. Increase test retries

    claudiodekker committed May 23, 2021
    Copy the full SHA
    2a41b54 View commit details

Commits on May 24, 2021

  1. Fix test

    claudiodekker committed May 24, 2021
    Copy the full SHA
    111a04e View commit details
  2. Copy the full SHA
    4c6e7db View commit details
  3. Merge pull request #689 from inertiajs/attempt-to-fix-dialogs

    Fix merge conflicts (Dialog PR)
    claudiodekker authored May 24, 2021
    Copy the full SHA
    fbeb77e View commit details
  4. Merge branch 'master' into dialogs

    # Conflicts:
    #	packages/inertia-vue3/src/app.js
    #	packages/inertia/src/router.ts
    #	packages/inertia/src/types.ts
    claudiodekker committed May 24, 2021
    Copy the full SHA
    7320b67 View commit details
  5. Remove blank space

    claudiodekker committed May 24, 2021
    Copy the full SHA
    1e69a3e View commit details

Commits on Jun 24, 2021

  1. Copy the full SHA
    5542c72 View commit details
  2. Merge branch 'master' into fix-dialogs-conflict

    # Conflicts:
    #	packages/inertia-vue3/src/app.js
    #	packages/inertia/src/router.ts
    #	packages/inertia/src/types.ts
    claudiodekker committed Jun 24, 2021
    Copy the full SHA
    77a552d View commit details
  3. Apply suggested changes

    claudiodekker committed Jun 24, 2021
    Copy the full SHA
    17d3048 View commit details

Commits on Sep 21, 2021

  1. Merge branch 'dialogs' of https://github.com/skore/inertia

    # Conflicts:
    #	packages/inertia/src/router.ts
    d8vjork committed Sep 21, 2021
    Copy the full SHA
    3c8a0a7 View commit details
Showing with 145 additions and 61 deletions.
  1. +1 −0 packages/inertia-vue/tests/cypress/integration/links.test.js
  2. +76 −27 packages/inertia-vue3/src/app.js
  3. +53 −31 packages/inertia/src/router.ts
  4. +15 −3 packages/inertia/src/types.ts
Original file line number Diff line number Diff line change
@@ -824,6 +824,7 @@ describe('Links', () => {

it('restores all tracked scroll regions when pressing the back button from another website', () => {
cy.get('.off-site')
.wait(50)
.click({ force: true })
.then(() => {
cy.url().should('eq', Cypress.config().baseUrl + '/non-inertia')
103 changes: 76 additions & 27 deletions packages/inertia-vue3/src/app.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import useForm from './useForm'
import remember from './remember'
import { computed, h, markRaw, ref } from 'vue'
import { computed, h, markRaw, ref, nextTick } from 'vue'
import { createHeadManager, Inertia } from '@inertiajs/inertia'
import cloneDeep from 'lodash.clonedeep'

const component = ref(null)
const page = ref({})
const key = ref(null)
const pageKey = ref(null)
const pageComponent = ref(null)

const dialog = ref({})
const dialogKey = ref(null)
const dialogComponent = ref(null)
let headManager = null

export default {
@@ -35,9 +40,9 @@ export default {
},
},
setup({ initialPage, initialComponent, resolveComponent, titleCallback, onHeadUpdate }) {
component.value = initialComponent ? markRaw(initialComponent) : null
pageComponent.value = initialComponent ? markRaw(initialComponent) : null
pageKey.value = null
page.value = initialPage
key.value = null

const isServer = typeof window === 'undefined'
headManager = createHeadManager(isServer, titleCallback, onHeadUpdate)
@@ -47,37 +52,80 @@ export default {
initialPage,
resolveComponent,
swapComponent: async (args) => {
component.value = markRaw(args.component)
page.value = args.page
key.value = args.preserveState ? key.value : Date.now()
const { dialog: _dialog, ..._page } = args.page

page.value = _page
pageKey.value = (args.preserveState || args.dialogComponent) ? pageKey.value : Date.now()
pageComponent.value = markRaw(args.component)

if (args.dialogComponent) {
nextTick(() => {
function shouldAppear() {
const newKey = [args.dialogComponent, ...Object.values(args.dialogComponent.components)].find(component => component.dialogKey)?.dialogKey
const currentKey = [dialogComponent.value || {}, ...Object.values(dialogComponent.value?.components || {})].find(component => component.dialogKey)?.dialogKey

return !_dialog.eager &&
!(dialog.value.open &&
(_dialog.component === dialog.value.component || (newKey && currentKey && newKey === currentKey))
)
}

dialog.value = { ...cloneDeep(_dialog), open: true, appear: shouldAppear() }
dialogKey.value = (args.preserveState && args.dialogComponent) ? dialogKey.value : Date.now()
dialogComponent.value = markRaw(args.dialogComponent)
})
} else if (dialog.value.open === true) {
dialog.value.open = false
}
},
})
}

return () => {
if (component.value) {
component.value.inheritAttrs = !!component.value.inheritAttrs
function renderPage() {
pageComponent.value.inheritAttrs = !!pageComponent.value.inheritAttrs

const child = h(component.value, {
...page.value.props,
key: key.value,
})
return h(pageComponent.value, {
...page.value.props,
dialog: false,
key: pageKey.value,
})
}

if (component.value.layout) {
if (typeof component.value.layout === 'function') {
return component.value.layout(h, child)
}
function renderLayout(child) {
if (typeof pageComponent.value.layout === 'function') {
return pageComponent.value.layout(h, child)
} else if (Array.isArray(pageComponent.value.layout)) {
return pageComponent.value.layout
.concat(child)
.reverse()
.reduce((child, layout) => {
layout.inheritAttrs = !!layout.inheritAttrs
return h(layout, { ...page.value.props }, () => child)
})
}

return (Array.isArray(component.value.layout) ? component.value.layout : [component.value.layout])
.concat(child)
.reverse()
.reduce((child, layout) => {
layout.inheritAttrs = !!layout.inheritAttrs
return h(layout, { ...page.value.props }, () => child)
})
return [
h(pageComponent.value.layout, { ...page.value.props }, () => child),
renderDialog(),
]
}

function renderDialog() {
return dialogComponent.value ? h(dialogComponent.value, {
...dialog.value.props,
key: dialogKey.value,
}) : null
}

return () => {
if (pageComponent.value) {
const page = renderPage()

if (pageComponent.value.layout) {
return renderLayout(page)
}

return child
return [page, renderDialog()]
}
}
},
@@ -89,6 +137,7 @@ export const plugin = {

Object.defineProperty(app.config.globalProperties, '$inertia', { get: () => Inertia })
Object.defineProperty(app.config.globalProperties, '$page', { get: () => page.value })
Object.defineProperty(app.config.globalProperties, '$dialog', { get: () => dialog.value })
Object.defineProperty(app.config.globalProperties, '$headManager', { get: () => headManager })

app.mixin(remember)
84 changes: 53 additions & 31 deletions packages/inertia/src/router.ts
Original file line number Diff line number Diff line change
@@ -83,15 +83,17 @@ export class Router {
}
}

protected restoreScrollPositions(): void {
protected restoreScrollPositions(attempt = 1): void {
if (this.page.scrollRegions) {
this.scrollRegions().forEach((region: Element, index: number) => {
const scrollPosition = this.page.scrollRegions[index]
if (scrollPosition) {
region.scrollTop = scrollPosition.top
region.scrollLeft = scrollPosition.left
}
})
const availableRegions = this.scrollRegions()
if (attempt <= 20 && (this.page.scrollRegions.length !== availableRegions.length)) {
Promise.resolve().then(() => this.restoreScrollPositions(attempt + 1))
} else {
return availableRegions.forEach((region: Element, index: number) => {
region.scrollTop = this.page.scrollRegions[index].top
region.scrollLeft = this.page.scrollRegions[index].left
})
}
}
}

@@ -275,6 +277,7 @@ export class Router {
Accept: 'text/html, application/xhtml+xml',
'X-Requested-With': 'XMLHttpRequest',
'X-Inertia': true,
'X-Inertia-Context': this.page.context,
...(only.length ? {
'X-Inertia-Partial-Component': this.page.component,
'X-Inertia-Partial-Data': only.join(','),
@@ -294,22 +297,31 @@ export class Router {
return Promise.reject({ response })
}

const pageResponse: Page = response.data
if (only.length && pageResponse.component === this.page.component) {
pageResponse.props = { ...this.page.props, ...pageResponse.props }
let page: Page = response.data
if (response.data.type === 'dialog') {
page = this.page
page.dialog = {
component: response.data.component,
props: response.data.props,
url: response.data.url,
eager: false,
}
}
if (only.length && page.component === this.page.component) {
page.props = { ...this.page.props, ...page.props }
}
preserveScroll = this.resolvePreserveOption(preserveScroll, pageResponse) as boolean
preserveState = this.resolvePreserveOption(preserveState, pageResponse)
if (preserveState && window.history.state?.rememberedState && pageResponse.component === this.page.component) {
pageResponse.rememberedState = window.history.state.rememberedState
preserveScroll = this.resolvePreserveOption(preserveScroll, page)
preserveState = this.resolvePreserveOption(preserveState, page)
if (preserveState && window.history.state?.rememberedState && page.component === this.page.component) {
page.rememberedState = window.history.state.rememberedState
}
const requestUrl = url
const responseUrl = hrefToUrl(pageResponse.url)
const responseUrl = hrefToUrl(page.url)
if (requestUrl.hash && !responseUrl.hash && urlWithoutHash(requestUrl).href === responseUrl.href) {
responseUrl.hash = requestUrl.hash
pageResponse.url = responseUrl.href
page.url = responseUrl.href
}
return this.setPage(pageResponse, { visitId, replace, preserveScroll, preserveState })
return this.setPage(page, { visitId, replace, preserveScroll, preserveState })
}).then(() => {
const errors = this.page.props.errors || {}
if (Object.keys(errors).length > 0) {
@@ -368,28 +380,30 @@ export class Router {
if (visitId === this.visitId) {
page.scrollRegions = page.scrollRegions || []
page.rememberedState = page.rememberedState || {}
replace = replace || hrefToUrl(page.url).href === window.location.href
replace = replace || hrefToUrl(page.dialog?.url || page.url).href === window.location.href
replace ? this.replaceState(page) : this.pushState(page)
this.swapComponent({ component, page, preserveState }).then(() => {
if (!preserveScroll) {
this.resetScrollPositions()
}
if (!replace) {
fireNavigateEvent(page)
}
Promise.resolve(page.dialog ? this.resolveComponent(page.dialog.component) : null).then(dialogComponent => {
this.swapComponent({ component, page, preserveState, dialogComponent }).then(() => {
if (!preserveScroll) {
this.resetScrollPositions()
}
if (!replace) {
fireNavigateEvent(page)
}
})
})
}
})
}

protected pushState(page: Page): void {
this.page = page
window.history.pushState(page, '', page.url)
window.history.pushState(page, '', page.dialog?.url || page.url)
}

protected replaceState(page: Page): void {
this.page = page
window.history.replaceState(page, '', page.url)
window.history.replaceState(page, '', page.dialog?.url || page.url)
}

protected handlePopstateEvent(event: PopStateEvent): void {
@@ -399,9 +413,11 @@ export class Router {
Promise.resolve(this.resolveComponent(page.component)).then(component => {
if (visitId === this.visitId) {
this.page = page
this.swapComponent({ component, page, preserveState: false }).then(() => {
this.restoreScrollPositions()
fireNavigateEvent(page)
Promise.resolve(page.dialog ? this.resolveComponent(page.dialog.component) : null).then(dialogComponent => {
this.swapComponent({component, page, preserveState: false, dialogComponent}).then(() => {
this.restoreScrollPositions()
fireNavigateEvent(page)
})
})
}
})
@@ -442,6 +458,12 @@ export class Router {
return this.visit(url, { preserveState: true, ...options, method: Method.DELETE })
}

public closeDialog(): void {
delete this.page.dialog
delete this.page.rememberedState
this.setPage(this.page)
}

public remember(data: unknown, key = 'default'): void {
this.replaceState({
...this.page,
18 changes: 15 additions & 3 deletions packages/inertia/src/types.ts
Original file line number Diff line number Diff line change
@@ -19,17 +19,27 @@ export interface PageProps {
[key: string]: unknown
}

export interface Page<SharedProps = PageProps> {
export interface PagePayload<SharedProps = PageProps> {
component: string,
props: PageProps & SharedProps & {
errors: Errors & ErrorBag;
}
url: string,
version: string|null
}

export interface DialogPayload extends PagePayload {
eager?: boolean
}

export interface Page<SharedProps = PageProps> extends PagePayload<SharedProps> {
type?: string,
dialog?: DialogPayload,
context: string,
version: string|null,

// Refactor away
scrollRegions: Array<{ top: number, left: number }>
rememberedState: Record<string, unknown>
rememberedState?: Record<string, unknown>
resolvedErrors: Errors
}

@@ -39,10 +49,12 @@ export type PageHandler = ({
component,
page,
preserveState,
dialogComponent,
}: {
component: Component,
page: Page,
preserveState: PreserveStateOption,
dialogComponent?: Component|null
}) => Promise<unknown>

export type PreserveStateOption = boolean|string|((page: Page) => boolean)