Skip to content

Commit b3e08a9

Browse files
Add option to destroy modal state (#95)
1 parent 51801f2 commit b3e08a9

9 files changed

Lines changed: 98 additions & 14 deletions

File tree

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,14 @@ By default, the modal will close when you click outside the modal. If you want t
270270
}
271271
```
272272

273+
By default, when a child modal is closed, the closed components state is still available if the same modal component is opened again. If you would like to destroy the component when its closed you can override the static `destroyOnClose` method and have it return `true`. When a destroyed modal is opened again its state will be reset.
274+
```php
275+
public static function destroyOnClose(): bool
276+
{
277+
return true;
278+
}
279+
```
280+
273281
## Skipping previous modals
274282
In some cases you might want to skip previous modals. For example:
275283
1. Team overview modal
@@ -314,6 +322,12 @@ class DeleteTeam extends ModalComponent
314322
}
315323
```
316324

325+
You can also optionally call the `destroySkippedModals()` method to destroy the skipped modals so if any are opened again their state will be reset
326+
327+
328+
329+
330+
317331
## Building Tailwind CSS for production
318332
To purge the classes used by the package, add the following lines to your purge array in `tailwind.config.js`:
319333
```js
@@ -406,6 +420,8 @@ return [
406420
'close_modal_on_escape_is_forceful' => true,
407421

408422
'dispatch_close_event' => false,
423+
424+
'destroy_on_close' => false,
409425
],
410426
];
411427
```

config/livewire-ui-modal.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,7 @@
4848
'close_modal_on_escape_is_forceful' => true,
4949

5050
'dispatch_close_event' => false,
51+
52+
'destroy_on_close' => false,
5153
],
5254
];

public/modal.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/js/modal.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,23 @@ window.LivewireUIModal = () => {
2525

2626
this.closeModal(true);
2727
},
28-
closeModal(force = false, skipPreviousModals = 0) {
28+
closeModal(force = false, skipPreviousModals = 0, destroySkipped = false) {
2929

3030
if (this.getActiveComponentModalAttribute('dispatchCloseEvent') === true) {
3131
const componentName = this.$wire.get('components')[this.activeComponent].name;
3232
Livewire.emit('modalClosed', componentName);
3333
}
3434

35+
if (this.getActiveComponentModalAttribute('destroyOnClose') === true) {
36+
Livewire.emit('destroyComponent', this.activeComponent);
37+
}
38+
3539
if (skipPreviousModals > 0) {
3640
for (var i = 0; i < skipPreviousModals; i++) {
41+
if (destroySkipped) {
42+
const id = this.componentHistory[this.componentHistory.length - 1];
43+
Livewire.emit('destroyComponent', id);
44+
}
3745
this.componentHistory.pop();
3846
}
3947
}
@@ -126,13 +134,15 @@ window.LivewireUIModal = () => {
126134
}
127135
});
128136

129-
Livewire.on('closeModal', (force = false, skipPreviousModals = 0) => {
130-
this.closeModal(force, skipPreviousModals);
137+
Livewire.on('closeModal', (force = false, skipPreviousModals = 0, destroySkipped = false) => {
138+
this.closeModal(force, skipPreviousModals, destroySkipped);
131139
});
132140

133141
Livewire.on('activeModalComponentChanged', (id) => {
134142
this.setActiveModalComponent(id);
135143
});
144+
145+
136146
}
137147
};
138148
}

resources/views/modal.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class="fixed inset-0 transition-all transform"
4646
class="inline-block w-full align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:w-full"
4747
>
4848
@forelse($components as $id => $component)
49-
<div x-show.immediate="activeComponent == '{{ $id }}'" x-ref="{{ $id }}">
49+
<div x-show.immediate="activeComponent == '{{ $id }}'" x-ref="{{ $id }}" wire:key="{{ $id }}">
5050
@livewire($component['name'], $component['attributes'], key($id))
5151
</div>
5252
@empty

src/Modal.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public function openModal($component, $componentAttributes = [], $modalAttribute
3838
'closeOnEscape' => $componentClass::closeModalOnEscape(),
3939
'closeOnEscapeIsForceful' => $componentClass::closeModalOnEscapeIsForceful(),
4040
'dispatchCloseEvent' => $componentClass::dispatchCloseEvent(),
41+
'destroyOnClose' => $componentClass::destroyOnClose(),
4142
'maxWidth' => $componentClass::modalMaxWidth(),
4243
], $modalAttributes),
4344
];
@@ -47,10 +48,16 @@ public function openModal($component, $componentAttributes = [], $modalAttribute
4748
$this->emit('activeModalComponentChanged', $id);
4849
}
4950

51+
public function destroyComponent($id): void
52+
{
53+
unset($this->components[$id]);
54+
}
55+
5056
public function getListeners(): array
5157
{
5258
return [
5359
'openModal',
60+
'destroyComponent'
5461
];
5562
}
5663

src/ModalComponent.php

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,26 @@ abstract class ModalComponent extends Component implements Contract
1111

1212
public int $skipModals = 0;
1313

14-
public function skipPreviousModals($count = 1): self
14+
public bool $destroySkipped = false;
15+
16+
public function destroySkippedModals(): self
17+
{
18+
$this->destroySkipped = true;
19+
20+
return $this;
21+
}
22+
23+
public function skipPreviousModals($count = 1, $destroy = false): self
1524
{
16-
$this->skipPreviousModal($count);
25+
$this->skipPreviousModal($count, $destroy);
1726

1827
return $this;
1928
}
2029

21-
public function skipPreviousModal($count = 1): self
30+
public function skipPreviousModal($count = 1, $destroy = false): self
2231
{
2332
$this->skipModals = $count;
33+
$this->destroySkipped = $destroy;
2434

2535
return $this;
2636
}
@@ -34,7 +44,7 @@ public function forceClose(): self
3444

3545
public function closeModal(): void
3646
{
37-
$this->emit('closeModal', $this->forceClose, $this->skipModals);
47+
$this->emit('closeModal', $this->forceClose, $this->skipModals, $this->destroySkipped);
3848
}
3949

4050
public function closeModalWithEvents(array $events): void
@@ -68,6 +78,11 @@ public static function dispatchCloseEvent(): bool
6878
return config('livewire-ui-modal.component_defaults.dispatch_close_event', false);
6979
}
7080

81+
public static function destroyOnClose(): bool
82+
{
83+
return config('livewire-ui-modal.component_defaults.destroy_on_close', false);
84+
}
85+
7186
private function emitModalEvents(array $events): void
7287
{
7388
foreach ($events as $component => $event) {

tests/LivewireModalComponentTest.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,34 @@ public function testCloseModal(): void
1111
{
1212
Livewire::test(DemoModal::class)
1313
->call('closeModal')
14-
->assertEmitted('closeModal', false, 0);
14+
->assertEmitted('closeModal', false, 0, false);
1515
}
1616

1717
public function testForceCloseModal(): void
1818
{
1919
Livewire::test(DemoModal::class)
2020
->call('forceClose')
2121
->call('closeModal')
22-
->assertEmitted('closeModal', true, 0);
22+
->assertEmitted('closeModal', true, 0, false);
2323
}
2424

2525
public function testModalSkipping(): void
2626
{
2727
Livewire::test(DemoModal::class)
2828
->call('skipPreviousModals', 5)
2929
->call('closeModal')
30-
->assertEmitted('closeModal', false, 5);
30+
->assertEmitted('closeModal', false, 5, false);
3131

3232
Livewire::test(DemoModal::class)
3333
->call('skipPreviousModal')
3434
->call('closeModal')
35-
->assertEmitted('closeModal', false, 1);
35+
->assertEmitted('closeModal', false, 1, false);
36+
37+
Livewire::test(DemoModal::class)
38+
->call('skipPreviousModal')
39+
->call('destroySkippedModals')
40+
->call('closeModal')
41+
->assertEmitted('closeModal', false, 1, true);
3642
}
3743

3844
public function testModalEventEmitting(): void

tests/LivewireModalTest.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use LivewireUI\Modal\Tests\Components\DemoModal;
88
use LivewireUI\Modal\Tests\Components\InvalidModal;
99

10+
use function PHPUnit\Framework\assertArrayNotHasKey;
11+
1012
class LivewireModalTest extends TestCase
1113
{
1214
public function testOpenModalEventListener(): void
@@ -17,7 +19,7 @@ public function testOpenModalEventListener(): void
1719
// Event attributes
1820
$component = 'demo-modal';
1921
$componentAttributes = ['message' => 'Foobar'];
20-
$modalAttributes = ['hello' => 'world', 'closeOnEscape' => true, 'maxWidth' => '2xl', 'closeOnClickAway' => true, 'closeOnEscapeIsForceful' => true, 'dispatchCloseEvent' => false];
22+
$modalAttributes = ['hello' => 'world', 'closeOnEscape' => true, 'maxWidth' => '2xl', 'closeOnClickAway' => true, 'closeOnEscapeIsForceful' => true, 'dispatchCloseEvent' => false, 'destroyOnClose' => false];
2123

2224
// Demo modal unique identifier
2325
$id = md5($component . serialize($componentAttributes));
@@ -38,6 +40,32 @@ public function testOpenModalEventListener(): void
3840
->assertEmitted('activeModalComponentChanged', $id);
3941
}
4042

43+
public function testDestroyComponentEventListener(): void
44+
{
45+
// Demo modal component
46+
Livewire::component('demo-modal', DemoModal::class);
47+
48+
$component = 'demo-modal';
49+
$componentAttributes = ['message' => 'Foobar'];
50+
$modalAttributes = ['hello' => 'world', 'closeOnEscape' => true, 'maxWidth' => '2xl', 'closeOnClickAway' => true, 'closeOnEscapeIsForceful' => true, 'dispatchCloseEvent' => false, 'destroyOnClose' => false];
51+
52+
// Demo modal unique identifier
53+
$id = md5($component . serialize($componentAttributes));
54+
55+
Livewire::test(Modal::class)
56+
->emit('openModal', $component, $componentAttributes, $modalAttributes)
57+
->assertSet('components', [
58+
$id => [
59+
'name' => $component,
60+
'attributes' => $componentAttributes,
61+
'modalAttributes' => $modalAttributes,
62+
],
63+
])
64+
->emit('destroyComponent', $id)
65+
->assertSet('components', []);
66+
67+
}
68+
4169
public function testModalReset(): void
4270
{
4371
Livewire::component('demo-modal', DemoModal::class);

0 commit comments

Comments
 (0)