2323 class =" fi-fo-record-select-input-wrp"
2424>
2525 <div
26- wire:key =" {{ $key } }"
26+ wire:key =" {{ $key } }- {{ $isDisabled ? ' disabled ' : ' enabled ' } } "
2727 wire:ignore.self
28+ x-cloak
2829 x-data =" {
2930 state: $wire.{{ $applyStateBindingModifiers (" \$ entangle('{$statePath }')" ) } } ,
31+ open: false,
3032 search: '',
3133 isSearching: false,
3234 searchResults: [],
@@ -39,6 +41,7 @@ class="fi-fo-record-select-input-wrp"
3941 maxVisibleValues: @js ($maxVisiblePills ),
4042 selectedSnapshot: [],
4143 activeIndex: -1,
44+ documentClickListener: null,
4245
4346 init() {
4447 if (!Array.isArray(this.state)) {
@@ -52,13 +55,42 @@ class="fi-fo-record-select-input-wrp"
5255 } else {
5356 this.searchResults = [];
5457 }
55- // Reset activeIndex when search changes
5658 this.activeIndex = this.sortedOptions.length > 0 ? 0 : -1;
5759 });
60+
61+ this.$watch('open', (isOpen) => {
62+ if (isOpen) {
63+ this.selectedSnapshot = [...this.state];
64+ this.search = '';
65+ this.searchResults = [];
66+ this.activeIndex = this.getInitialActiveIndex();
67+ this.$nextTick(() => {
68+ this.$refs.searchInput?.focus();
69+ this.scrollActiveIntoView();
70+ });
71+ } else {
72+ this.search = '';
73+ this.searchResults = [];
74+ this.activeIndex = -1;
75+ }
76+ });
77+
78+ this.documentClickListener = (event) => {
79+ if (this.open && !this.$el.contains(event.target)) {
80+ this.close();
81+ }
82+ };
83+ document.addEventListener('click', this.documentClickListener);
84+ },
85+
86+ destroy() {
87+ if (this.documentClickListener) {
88+ document.removeEventListener('click', this.documentClickListener);
89+ }
5890 },
5991
6092 get activeDescendant() {
61- if (!this.isOpen() || this.activeIndex < 0 || this.activeIndex >= this.sortedOptions.length) {
93+ if (!this.open || this.activeIndex < 0 || this.activeIndex >= this.sortedOptions.length) {
6294 return null;
6395 }
6496 return this.$id('option-' + this.activeIndex);
@@ -165,55 +197,26 @@ class="fi-fo-record-select-input-wrp"
165197 }
166198 },
167199
168- isOpen() {
169- return this.$refs.panel?._x_isShown === true;
170- },
171-
172- togglePanel() {
200+ toggle() {
173201 if (this.isDisabled) return;
174-
175- const wasOpen = this.isOpen();
176- this.$refs.panel?.toggle(this.$refs.trigger);
177-
178- if (!wasOpen) {
179- // Opening the panel
180- this.selectedSnapshot = [...this.state];
181- this.search = '';
182- this.searchResults = [];
183- this.activeIndex = this.getInitialActiveIndex();
184- // Use setTimeout to ensure panel transition has started
185- setTimeout(() => {
186- this.$refs.searchInput?.focus();
187- this.scrollActiveIntoView();
188- }, 50);
189- } else {
190- this.activeIndex = -1;
191- }
202+ this.open ? this.close() : this.openPanel();
192203 },
193204
194205 openPanel() {
195- if (this.isDisabled) return;
196- this.selectedSnapshot = [...this.state];
206+ if (this.isDisabled || this.open) return;
197207 this.$refs.panel?.open(this.$refs.trigger);
198- this.search = '';
199- this.searchResults = [];
200- this.activeIndex = this.getInitialActiveIndex();
201- // Use setTimeout to ensure panel transition has started
202- setTimeout(() => {
203- this.$refs.searchInput?.focus();
204- this.scrollActiveIntoView();
205- }, 50);
208+ this.open = true;
206209 },
207210
208- closePanel () {
209- const wasOpen = this.isOpen() ;
211+ close () {
212+ if (! this.open) return ;
210213 this.$refs.panel?.close();
211- this.search = '' ;
212- this.searchResults = [] ;
213- this.activeIndex = -1;
214- if (wasOpen) {
215- this.$refs.trigger?.focus();
216- }
214+ this.open = false ;
215+ this.$refs.trigger?.focus() ;
216+ },
217+
218+ closePanel() {
219+ this.close();
217220 },
218221
219222 onKeydown(event) {
@@ -223,7 +226,7 @@ class="fi-fo-record-select-input-wrp"
223226 case 'ArrowDown':
224227 event.preventDefault();
225228 event.stopPropagation();
226- if (this.isOpen() ) {
229+ if (this.open ) {
227230 this.focusNext();
228231 } else {
229232 this.openPanel();
@@ -232,46 +235,45 @@ class="fi-fo-record-select-input-wrp"
232235 case 'ArrowUp':
233236 event.preventDefault();
234237 event.stopPropagation();
235- if (this.isOpen() ) {
238+ if (this.open ) {
236239 this.focusPrevious();
237240 } else {
238241 this.openPanel();
239242 }
240243 break;
241244 case 'Home':
242- if (this.isOpen() ) {
245+ if (this.open ) {
243246 event.preventDefault();
244247 this.focusFirst();
245248 }
246249 break;
247250 case 'End':
248- if (this.isOpen() ) {
251+ if (this.open ) {
249252 event.preventDefault();
250253 this.focusLast();
251254 }
252255 break;
253256 case 'Enter':
254257 event.preventDefault();
255- if (this.isOpen() && this.activeIndex >= 0 && this.activeIndex < this.sortedOptions.length) {
258+ if (this.open && this.activeIndex >= 0 && this.activeIndex < this.sortedOptions.length) {
256259 const record = this.sortedOptions[this.activeIndex];
257260 this.allowMultiple ? this.toggleRecord(record) : this.selectRecord(record);
258- } else if (!this.isOpen() ) {
261+ } else if (!this.open ) {
259262 this.openPanel();
260263 }
261264 break;
262265 case ' ':
263- // Don't intercept space when typing in search input
264266 if (document.activeElement === this.$refs.searchInput) {
265267 return;
266268 }
267- if (!this.isOpen() ) {
269+ if (!this.open ) {
268270 event.preventDefault();
269271 this.openPanel();
270272 }
271273 break;
272274 case 'Tab':
273- if (this.isOpen() ) {
274- this.closePanel ();
275+ if (this.open ) {
276+ this.close ();
275277 }
276278 break;
277279 }
@@ -399,8 +401,8 @@ class="fi-fo-record-select-input-wrp"
399401 this.state = this.state.filter(id => id !== recordId);
400402 }
401403 }"
402- x-on:click.outside =" closePanel ()"
403- x-on:keydown.esc =" isOpen() && (closePanel (), $event.stopPropagation())"
404+ x-on:click.outside =" close ()"
405+ x-on:keydown.esc =" open && (close (), $event.stopPropagation())"
404406 x-on:keydown =" onKeydown($event)"
405407 class =" relative w-full"
406408 >
@@ -414,18 +416,19 @@ class="relative w-full"
414416 \Filament\Support\prepare_inherited_attributes($attributes)
415417 ->class(['fi-fo-record-select-input'])
416418 "
419+ x-bind:class =" { 'ring-2 ring-primary-600 dark:ring-primary-500': open }"
417420 >
418421 {{-- Single Value Mode --}}
419422 <template x-if =" !allowMultiple" >
420423 <button
421424 type =" button"
422425 x-ref =" trigger"
423- x-on:click =" togglePanel ()"
424- x-on:keydown.enter.prevent =" togglePanel ()"
425- x-on:keydown.space.prevent =" togglePanel ()"
426+ x-on:click =" toggle ()"
427+ x-on:keydown.enter.prevent =" toggle ()"
428+ x-on:keydown.space.prevent =" toggle ()"
426429 :disabled =" isDisabled"
427430 role =" combobox"
428- :aria-expanded =" isOpen() ? 'true' : 'false'"
431+ :aria-expanded =" open ? 'true' : 'false'"
429432 aria-haspopup =" listbox"
430433 :aria-controls =" $id('panel')"
431434 :aria-activedescendant =" activeDescendant"
@@ -466,7 +469,7 @@ class="shrink-0 rounded p-0.5 text-gray-400 hover:text-gray-500 dark:hover:text-
466469 {{-- Chevron --}}
467470 <x-heroicon-m-chevron-down
468471 class =" size-4 text-gray-400 dark:text-gray-500 shrink-0 transition-transform duration-200"
469- x-bind:class =" { 'rotate-180': isOpen() }"
472+ x-bind:class =" { 'rotate-180': open }"
470473 />
471474 </button >
472475 </template >
@@ -477,12 +480,12 @@ class="size-4 text-gray-400 dark:text-gray-500 shrink-0 transition-transform dur
477480 <button
478481 type =" button"
479482 x-ref =" trigger"
480- x-on:click =" togglePanel ()"
481- x-on:keydown.enter.prevent =" togglePanel ()"
482- x-on:keydown.space.prevent =" togglePanel ()"
483+ x-on:click =" toggle ()"
484+ x-on:keydown.enter.prevent =" toggle ()"
485+ x-on:keydown.space.prevent =" toggle ()"
483486 :disabled =" isDisabled"
484487 role =" combobox"
485- :aria-expanded =" isOpen() ? 'true' : 'false'"
488+ :aria-expanded =" open ? 'true' : 'false'"
486489 aria-haspopup =" listbox"
487490 :aria-controls =" $id('panel')"
488491 :aria-activedescendant =" activeDescendant"
@@ -532,7 +535,7 @@ class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
532535 {{-- Chevron --}}
533536 <x-heroicon-m-chevron-down
534537 class =" size-4 text-gray-400 dark:text-gray-500 shrink-0 transition-transform duration-200"
535- x-bind:class =" { 'rotate-180': isOpen() }"
538+ x-bind:class =" { 'rotate-180': open }"
536539 />
537540 </button >
538541 </div >
0 commit comments