diff --git a/demo-app/resources/js/Pages/Local.jsx b/demo-app/resources/js/Pages/Local.jsx
index 709899d..df72e62 100644
--- a/demo-app/resources/js/Pages/Local.jsx
+++ b/demo-app/resources/js/Pages/Local.jsx
@@ -1,7 +1,18 @@
import { Modal, ModalLink } from '@inertiaui/modal-react';
import Container from './Container';
+import { useRef } from 'react';
export default function Local() {
+ const modalRef = useRef(null);
+
+ function closeModal() {
+ modalRef.current.close();
+ }
+
+ function alertModalId() {
+ alert(modalRef.current.id);
+ }
+
return (
<>
@@ -15,11 +26,17 @@ export default function Local() {
-
+
This is a local modal
Create Role
+
+
>
);
diff --git a/demo-app/resources/js/Pages/Local.vue b/demo-app/resources/js/Pages/Local.vue
index ecdd684..dc6f1ab 100644
--- a/demo-app/resources/js/Pages/Local.vue
+++ b/demo-app/resources/js/Pages/Local.vue
@@ -1,6 +1,17 @@
@@ -14,11 +25,19 @@ import { Modal, ModalLink } from '@inertiaui/modal-vue'
-
+
This is a local modal
Create Role
+
+
+
+
diff --git a/demo-app/tests/Browser/LocalModalTest.php b/demo-app/tests/Browser/LocalModalTest.php
index c862448..c22265c 100644
--- a/demo-app/tests/Browser/LocalModalTest.php
+++ b/demo-app/tests/Browser/LocalModalTest.php
@@ -20,4 +20,33 @@ public function it_can_open_a_local_modal_and_a_nested_one()
->waitUntilMissingModal(1);
});
}
+
+ #[Test]
+ public function it_can_access_a_prop_through_a_template_ref()
+ {
+ $this->browse(function (Browser $browser) {
+ $browser->visit('/local')
+ ->clickLink('Open Local Modal')
+ ->waitForTextIn('.im-modal-content', 'This is a local modal')
+ ->press('Alert Modal ID');
+
+ $message = $browser->driver->switchTo()->alert()->getText();
+
+ $this->assertStringStartsWith('inertiaui_modal_', $message);
+
+ $browser->dismissDialog();
+ });
+ }
+
+ #[Test]
+ public function it_can_close_a_local_modal_through_a_template_ref()
+ {
+ $this->browse(function (Browser $browser) {
+ $browser->visit('/local')
+ ->clickLink('Open Local Modal')
+ ->waitForTextIn('.im-modal-content', 'This is a local modal')
+ ->press('Close Modal through Ref')
+ ->waitUntilMissingModal(1);
+ });
+ }
}
diff --git a/demo-app/tests/Feature/DispatchBaseUrlRequestTest.php b/demo-app/tests/Feature/DispatchBaseUrlRequestTest.php
index fcec9c1..fef3710 100644
--- a/demo-app/tests/Feature/DispatchBaseUrlRequestTest.php
+++ b/demo-app/tests/Feature/DispatchBaseUrlRequestTest.php
@@ -15,7 +15,7 @@ class DispatchBaseUrlRequestTest extends TestCase
{
protected DispatchBaseUrlRequest $dispatcher;
- public function setUp(): void
+ protected function setUp(): void
{
parent::setUp();
diff --git a/demo-app/tests/Feature/RedirectorTest.php b/demo-app/tests/Feature/RedirectorTest.php
index c7e370b..60589ac 100644
--- a/demo-app/tests/Feature/RedirectorTest.php
+++ b/demo-app/tests/Feature/RedirectorTest.php
@@ -17,7 +17,7 @@ class RedirectorTest extends TestCase
protected Request $request;
- public function setUp(): void
+ protected function setUp(): void
{
parent::setUp();
$this->urlGenerator = app(UrlGenerator::class);
diff --git a/demo-app/tests/TestCase.php b/demo-app/tests/TestCase.php
index b66e347..33489d6 100644
--- a/demo-app/tests/TestCase.php
+++ b/demo-app/tests/TestCase.php
@@ -12,7 +12,7 @@ abstract class TestCase extends BaseTestCase
use ModalTestCase;
use RefreshDatabase;
- public function setUp(): void
+ protected function setUp(): void
{
parent::setUp();
@@ -20,7 +20,7 @@ public function setUp(): void
Carbon::setTestNow('2024-06-01 12:00:00');
}
- public function tearDown(): void
+ protected function tearDown(): void
{
Carbon::setTestNow();
parent::tearDown();
diff --git a/react/src/HeadlessModal.jsx b/react/src/HeadlessModal.jsx
index 7951221..dab2cce 100644
--- a/react/src/HeadlessModal.jsx
+++ b/react/src/HeadlessModal.jsx
@@ -1,4 +1,4 @@
-import { useMemo, useState, forwardRef, useImperativeHandle, useEffect } from 'react'
+import { useMemo, useState, forwardRef, useImperativeHandle, useEffect, useRef } from 'react'
import { getConfig, getConfigByType } from './config'
import { useModalIndex } from './ModalRenderer.jsx'
import { useModalStack } from './ModalRoot.jsx'
@@ -50,23 +50,46 @@ const HeadlessModal = forwardRef(({ name, children, ...props }, ref) => {
return modalContext.registerEventListenersFromProps(props)
}, [name])
+ // Store the latest modalContext in a ref to maintain reference
+ const modalContextRef = useRef(modalContext)
+
+ // Update the ref whenever modalContext changes
+ useEffect(() => {
+ modalContextRef.current = modalContext
+ }, [modalContext])
+
useImperativeHandle(
ref,
() => ({
- afterLeave: () => modalContext.afterLeave(),
- close: () => modalContext.close(),
- config,
- emit: (...args) => modalContext.emit(...args),
- getChildModal: () => modalContext.getChildModal(),
- getParentModal: () => modalContext.getParentModal(),
- id: modalContext?.id,
- index: modalContext?.index,
- isOpen: modalContext?.isOpen,
- modalContext,
- onTopOfStack: modalContext?.onTopOfStack,
- reload: () => modalContext.reload(),
- setOpen: () => modalContext.setOpen(),
- shouldRender: modalContext?.shouldRender,
+ afterLeave: () => modalContextRef.current?.afterLeave(),
+ close: () => modalContextRef.current?.close(),
+ emit: (...args) => modalContextRef.current?.emit(...args),
+ getChildModal: () => modalContextRef.current?.getChildModal(),
+ getParentModal: () => modalContextRef.current?.getParentModal(),
+ reload: (...args) => modalContextRef.current?.reload(...args),
+ setOpen: () => modalContextRef.current?.setOpen(),
+
+ get id() {
+ return modalContextRef.current?.id
+ },
+ get index() {
+ return modalContextRef.current?.index
+ },
+ get isOpen() {
+ return modalContextRef.current?.isOpen
+ },
+ get config() {
+ return modalContextRef.current?.config
+ },
+ get modalContext() {
+ return modalContextRef.current
+ },
+ get onTopOfStack() {
+ return modalContextRef.current?.onTopOfStack
+ },
+ get shouldRender() {
+ return modalContextRef.current?.shouldRender
+ },
}),
[modalContext],
)
diff --git a/vue/src/HeadlessModal.vue b/vue/src/HeadlessModal.vue
index c39f3e6..a74e85e 100644
--- a/vue/src/HeadlessModal.vue
+++ b/vue/src/HeadlessModal.vue
@@ -92,20 +92,35 @@ function emit(event, ...args) {
}
defineExpose({
- afterLeave: modalContext.value.afterLeave,
- close: modalContext.value.close,
- config: config.value,
emit,
- getChildModal: modalContext.value.getChildModal,
- getParentModal: modalContext.value.getParentModal,
- id: modalContext.value.id,
- index: modalContext.value.index,
- isOpen: modalContext.value.isOpen,
- modalContext: modalContext.value,
- onTopOfStack: modalContext.value.onTopOfStack,
- reload: modalContext.value.reload,
- setOpen: modalContext.value.setOpen,
- shouldRender: modalContext.value.shouldRender,
+ afterLeave: () => modalContext.value?.afterLeave(),
+ close: () => modalContext.value?.close(),
+ reload: (...args) => modalContext.value?.reload(...args),
+ setOpen: (...args) => modalContext.value?.setOpen(...args),
+ getChildModal: () => modalContext.value?.getChildModal(),
+ getParentModal: () => modalContext.value?.getParentModal(),
+
+ get config() {
+ return modalContext.value?.config
+ },
+ get id() {
+ return modalContext.value?.id
+ },
+ get index() {
+ return modalContext.value?.index
+ },
+ get isOpen() {
+ return modalContext.value?.isOpen
+ },
+ get modalContext() {
+ return modalContext.value?.modalContext
+ },
+ get onTopOfStack() {
+ return modalContext.value?.onTopOfStack
+ },
+ get shouldRender() {
+ return modalContext.value?.shouldRender
+ },
})
const nextIndex = computed(() => {
diff --git a/vue/src/Modal.vue b/vue/src/Modal.vue
index 19414ca..8d5b649 100644
--- a/vue/src/Modal.vue
+++ b/vue/src/Modal.vue
@@ -3,7 +3,7 @@ import { DialogOverlay, DialogPortal, DialogRoot } from 'radix-vue'
import ModalContent from './ModalContent.vue'
import HeadlessModal from './HeadlessModal.vue'
import SlideoverContent from './SlideoverContent.vue'
-import { computed, ref } from 'vue'
+import { ref } from 'vue'
const modal = ref(null)
const rendered = ref(false)
@@ -11,18 +11,33 @@ const rendered = ref(false)
defineExpose({
afterLeave: () => modal.value?.afterLeave(),
close: () => modal.value?.close(),
- config: computed(() => modal.value?.config),
emit: (...args) => modal.value?.emit(...args),
getChildModal: () => modal.value?.getChildModal(),
getParentModal: () => modal.value?.getParentModal(),
- id: computed(() => modal.value?.id),
- index: computed(() => modal.value?.index),
- isOpen: computed(() => modal.value?.isOpen),
- modalContext: computed(() => modal.value?.modalContext),
- onTopOfStack: computed(() => modal.value?.onTopOfStack),
reload: (...args) => modal.value?.reload(...args),
setOpen: (...args) => modal.value?.setOpen(...args),
- shouldRender: computed(() => modal.value?.shouldRender),
+
+ get config() {
+ return modal.value?.config
+ },
+ get id() {
+ return modal.value?.id
+ },
+ get index() {
+ return modal.value?.index
+ },
+ get isOpen() {
+ return modal.value?.isOpen
+ },
+ get modalContext() {
+ return modal.value?.modalContext
+ },
+ get onTopOfStack() {
+ return modal.value?.onTopOfStack
+ },
+ get shouldRender() {
+ return modal.value?.shouldRender
+ },
})