|
27 | 27 | <div |
28 | 28 | x-data="{ |
29 | 29 | show: @entangle($attributes->wire('model')).defer, |
| 30 | + focusables() { |
| 31 | + // All focusable element types... |
| 32 | + let selector = 'a, button, input, textarea, select, details, [tabindex]:not([tabindex=\'-1\'])' |
| 33 | + return [...$el.querySelectorAll(selector)] |
| 34 | + // All non-disabled elements... |
| 35 | + .filter(el => ! el.hasAttribute('disabled')) |
| 36 | + }, |
| 37 | + firstFocusable() { return this.focusables()[0] }, |
| 38 | + lastFocusable() { return this.focusables().slice(-1)[0] }, |
| 39 | + nextFocusable() { return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable() }, |
| 40 | + prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() }, |
| 41 | + nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) }, |
| 42 | + prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) -1 }, |
30 | 43 | }" |
31 | 44 | x-init="() => { |
32 | 45 | let modal = $('#{{ $id }}'); |
33 | 46 | $watch('show', value => { |
34 | 47 | if (value) { |
35 | 48 | modal.modal('show') |
| 49 | + setTimeout(() => (focusables()[0]).focus(), 250); |
36 | 50 | } else { |
37 | 51 | modal.modal('hide') |
38 | 52 | } |
|
41 | 55 | modal.on('hide.bs.modal', function () { |
42 | 56 | show = false |
43 | 57 | }) |
| 58 | + |
| 59 | + modal.click(function(e) { |
| 60 | + if (e.target == this) { |
| 61 | + show = false; |
| 62 | + } |
| 63 | + }); |
44 | 64 | }" |
| 65 | + x-on:keydown.escape.window="show = false" |
| 66 | + x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()" |
| 67 | + x-on:keydown.shift.tab.prevent="prevFocusable().focus()" |
45 | 68 | wire:ignore.self |
46 | 69 | class="modal fade" |
47 | | - tabindex="-1" |
48 | 70 | id="{{ $id }}" |
49 | 71 | aria-labelledby="{{ $id }}" |
50 | 72 | aria-hidden="true" |
|
0 commit comments