Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
pascalbaljet committed Jan 8, 2025
1 parent 1a623da commit 392ade5
Show file tree
Hide file tree
Showing 12 changed files with 100 additions and 70 deletions.
19 changes: 13 additions & 6 deletions demo-app/resources/js/Pages/Events.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { ModalLink } from '@inertiaui/modal-vue'
const log = ref([])
function push(value) {
log.value.push(value)
}
defineProps({
navigate: Boolean,
})
Expand All @@ -15,6 +20,8 @@ defineProps({
<div class="flex justify-between">
<h2 class="text-lg font-medium text-gray-900">Events</h2>

<p>Page ID: {{ $page.props._inertiaui_modal_page_id }}</p>

<p dusk="log">{{ log.join(',') }}</p>
</div>

Expand All @@ -23,12 +30,12 @@ defineProps({
dusk="modal-link"
href="/users/1/edit"
class="px-2 py-1 text-xs font-medium text-indigo-600 bg-indigo-100 rounded-md"
@close="log.push('close')"
@focus="log.push('focus')"
@after-leave="log.push('after-leave')"
@blur="log.push('blur')"
@start="log.push('start')"
@success="log.push('success')"
@close="push('close')"
@focus="push('focus')"
@after-leave="push('after-leave')"
@blur="push('blur')"
@start="push('start')"
@success="push('success')"
>
Open Modal
</ModalLink>
Expand Down
19 changes: 19 additions & 0 deletions demo-app/tests/Browser/ModalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Tests\Browser;

use App\Models\User;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use Tests\DuskTestCase;

Expand All @@ -25,6 +26,24 @@ public function it_can_open_the_modal_and_close_it_with_the_close_button()
});
}

#[DataProvider('booleanProvider')]
#[Test]
public function it_can_open_the_slideover_and_close_it_with_the_close_button(bool $navigate)
{
$this->browse(function (Browser $browser) use ($navigate) {
$firstUser = User::orderBy('name')->first();

$browser->visit('/users'.($navigate ? '?navigate=1' : ''))
->waitForFirstUser()
->click("@slideover-user-{$firstUser->id}")
->waitForModal()
->assertSeeIn('.im-slideover-content', 'Edit User')
->clickModalCloseButton()
->waitUntilMissingModal()
->assertMissing('div[data-inertiaui-modal-id]');
});
}

#[Test]
public function it_can_close_the_modal_by_clicking_outside_of_it()
{
Expand Down
1 change: 1 addition & 0 deletions demo-app/tests/Feature/ModalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public function it_returns_the_base_page_with_the_modal_as_a_separate_prop_when_
->has('_inertiaui_modal', fn (AssertableInertia $assert) => $assert
->where('component', 'EditUser')
->has('props')
->has('id')
->has('viaInertiaRouter')
->where('props.user.id', $user->id)
->has('version')
Expand Down
2 changes: 1 addition & 1 deletion react/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@inertiaui/modal-react",
"author": "Pascal Baljet <pascal@protone.media>",
"version": "0.15.0",
"version": "0.17.0",
"private": false,
"license": "MIT",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion src/Testing/DuskModalMacros.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class DuskModalMacros
*/
public function waitForModal(): Closure
{
return fn (int $index = 0, ?int $seconds = null): Browser => $this->waitFor('.im-dialog[data-inertiaui-modal-index="'.$index.'"]', $seconds);
return fn (int $index = 0, ?int $seconds = null): Browser => $this->waitFor('.im-dialog[data-inertiaui-modal-index="'.$index.'"] div[data-inertiaui-modal-entered="true"]', $seconds);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion vue/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@inertiaui/modal-vue",
"version": "0.15.0",
"version": "0.17.0",
"author": "Pascal Baljet <pascal@protone.media>",
"private": false,
"license": "MIT",
Expand Down
15 changes: 12 additions & 3 deletions vue/src/ModalContent.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup>
import { ref } from 'vue'
import { onBeforeUnmount, ref } from 'vue'
import CloseButton from './CloseButton.vue'
import { useFocusTrap } from './useFocusTrap'
Expand All @@ -8,8 +8,16 @@ const props = defineProps({
config: Object,
})
const entered = ref(false)
const wrapper = ref(null)
const focusTrap = () => useFocusTrap(wrapper.value, props.config?.closeExplicitly, () => props.modalContext.close())
let deactivate = null
function afterEnter() {
let { deactivate } = useFocusTrap(wrapper.value, props.config?.closeExplicitly, () => props.modalContext.close())
entered.value = true
}
onBeforeUnmount(() => deactivate?.())
</script>
<template>
Expand All @@ -30,7 +38,7 @@ const focusTrap = () => useFocusTrap(wrapper.value, props.config?.closeExplicitl
enter-to-class="opacity-100 translate-y-0 sm:scale-100"
leave-from-class="opacity-100 translate-y-0 sm:scale-100"
leave-to-class="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
@after-enter="focusTrap"
@after-enter="afterEnter"
@after-leave="modalContext.afterLeave"
>
<div
Expand All @@ -53,6 +61,7 @@ const focusTrap = () => useFocusTrap(wrapper.value, props.config?.closeExplicitl
>
<div
class="im-modal-content relative"
:data-inertiaui-modal-entered="entered"
:class="[config.paddingClasses, config.panelClasses]"
>
<div
Expand Down
3 changes: 1 addition & 2 deletions vue/src/ModalLink.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup>
import { modalPropNames, useModalStack } from './modalStack'
import { ref, provide, computed, watch, useAttrs, onBeforeUnmount, watchEffect } from 'vue'
import { ref, provide, computed, watch, useAttrs, onBeforeUnmount } from 'vue'
import { generateId, only, rejectNullValues } from './helpers'
import { getConfig } from './config'
Expand Down Expand Up @@ -115,7 +115,6 @@ watch(
const unsubscribeEventListeners = ref(null)
onBeforeUnmount(() => {
modalStack.removePendingModalUpdate(modalId.value)
unsubscribeEventListeners.value?.()
})
Expand Down
30 changes: 12 additions & 18 deletions vue/src/ModalRoot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,19 @@ onUnmounted(
previousModalOnBase.value = modalOnBase
modalStack.setBaseUrl(modalOnBase.baseUrl)
modalStack.pushFromResponseData(
modalOnBase,
{},
() => {
if (!modalOnBase.baseUrl) {
console.error('No base url in modal response data so cannot navigate back')
return
}
modalStack.pushFromResponseData(modalOnBase, {}, () => {
if (!modalOnBase.baseUrl) {
console.error('No base url in modal response data so cannot navigate back')
return
}
if (!isNavigating.value && window.location.href !== modalOnBase.baseUrl) {
router.visit(modalOnBase.baseUrl, {
preserveScroll: true,
preserveState: true,
})
}
},
null,
modalOnBase.viaInertiaRouter,
)
if (!isNavigating.value && window.location.href !== modalOnBase.baseUrl) {
router.visit(modalOnBase.baseUrl, {
preserveScroll: true,
preserveState: true,
})
}
})
}),
)
Expand Down
15 changes: 12 additions & 3 deletions vue/src/SlideoverContent.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup>
import { ref } from 'vue'
import { onBeforeUnmount, ref } from 'vue'
import CloseButton from './CloseButton.vue'
import { useFocusTrap } from './useFocusTrap'
Expand All @@ -8,8 +8,16 @@ const props = defineProps({
config: Object,
})
const entered = ref(false)
const wrapper = ref(null)
const focusTrap = () => useFocusTrap(wrapper.value, props.config?.closeExplicitly, () => props.modalContext.close())
let deactivate = null
function afterEnter() {
let { deactivate } = useFocusTrap(wrapper.value, props.config?.closeExplicitly, () => props.modalContext.close())
entered.value = true
}
onBeforeUnmount(() => deactivate?.())
</script>
<template>
Expand All @@ -29,7 +37,7 @@ const focusTrap = () => useFocusTrap(wrapper.value, props.config?.closeExplicitl
enter-to-class="opacity-100 translate-x-0"
leave-from-class="opacity-100 translate-x-0"
:leave-to-class="'opacity-0 ' + (config.position === 'left' ? '-translate-x-full' : 'translate-x-full')"
@after-enter="focusTrap"
@after-enter="afterEnter"
@after-leave="modalContext.afterLeave"
>
<div
Expand All @@ -52,6 +60,7 @@ const focusTrap = () => useFocusTrap(wrapper.value, props.config?.closeExplicitl
>
<div
class="im-slideover-content relative"
:data-inertiaui-modal-entered="entered"
:class="[config.paddingClasses, config.panelClasses]"
>
<div
Expand Down
54 changes: 22 additions & 32 deletions vue/src/modalStack.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { computed, readonly, ref, markRaw, nextTick, h } from 'vue'
import { computed, readonly, ref, markRaw, h } from 'vue'
import { generateId, except, only, waitFor, kebabCase } from './helpers'
import { router } from '@inertiajs/vue3'
import { usePage } from '@inertiajs/vue3'
Expand Down Expand Up @@ -47,14 +47,25 @@ class Modal {
...(pendingModalUpdates.value[this.id].config ?? {}),
}

this.onCloseCallback = () => {
onClose?.()
pendingModalUpdates.value[this.id].onClose?.()
const pendingOnClose = pendingModalUpdates.value[this.id].onClose
const pendingOnAfterLeave = pendingModalUpdates.value[this.id].onAfterLeave

if (pendingOnClose) {
this.onCloseCallback = onClose
? () => {
onClose()
pendingOnClose()
}
: pendingOnClose
}

this.afterLeaveCallback = () => {
afterLeave?.()
pendingModalUpdates.value[this.id].afterLeave?.()
if (pendingOnAfterLeave) {
this.afterLeaveCallback = afterLeave
? () => {
afterLeave()
pendingOnAfterLeave()
}
: pendingOnAfterLeave
}
}

Expand Down Expand Up @@ -237,10 +248,8 @@ function pushLocalModal(name, config, onClose, afterLeave) {
return modal
}

function pushFromResponseData(responseData, config = {}, onClose = null, onAfterLeave = null, viaInertiaRouter = false) {
return resolveComponent(responseData.component).then((component) =>
push(markRaw(component), responseData, config, onClose, onAfterLeave, viaInertiaRouter),
)
function pushFromResponseData(responseData, config = {}, onClose = null, onAfterLeave = null) {
return resolveComponent(responseData.component).then((component) => push(markRaw(component), responseData, config, onClose, onAfterLeave))
}

function visit(
Expand Down Expand Up @@ -293,26 +302,7 @@ function visit(
preserveScroll: true,
preserveState: true,
onError: reject,
onFinish: () =>
waitFor(() => stack.value[0]).then((modal) => {
// const originalOnClose = modal.onCloseCallback
// const originalAfterLeave = modal.afterLeaveCallback

// modal.update(
// config,
// () => {
// onClose?.()
// originalOnClose?.()
// },
// () => {
// onAfterLeave?.()
// originalAfterLeave?.()
// },
// )

// modal.show()
resolve(modal)
}),
onFinish: () => waitFor(() => stack.value[0]).then(resolve),
})
}

Expand All @@ -322,7 +312,7 @@ function visit(
})
}

function push(component, response, config, onClose, afterLeave, viaInertiaRouter = false) {
function push(component, response, config, onClose, afterLeave) {
const newModal = new Modal(component, response, config, onClose, afterLeave)
stack.value.push(newModal)
newModal.show()
Expand Down
8 changes: 5 additions & 3 deletions vue/src/useFocusTrap.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { onBeforeUnmount, ref } from 'vue'
import { ref } from 'vue'
import { createFocusTrap } from 'focus-trap'

export function useFocusTrap(wrapper, closeExplicitly, onDeactivateCallback) {
Expand All @@ -15,11 +15,13 @@ export function useFocusTrap(wrapper, closeExplicitly, onDeactivateCallback) {
trap.value.activate()
}

onBeforeUnmount(() => {
const deactivate = () => {
trap.value?.deactivate()
})
trap.value = null
}

return {
deactivate,
wrapper,
}
}

0 comments on commit 392ade5

Please sign in to comment.