PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 2.0.25
Advanced Ads – Ad Manager & AdSense v2.0.25
2.0.25 2.0.24 2.0.23 2.0.22 2.0.21 1.38.0 1.39.0 1.39.1 1.39.2 1.39.3 1.39.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.42.1 1.42.2 1.43.0 1.44.0 1.44.1 1.45.0 1.45.1 1.46.0 1.47.0 1.47.1 1.47.2 1.47.3 1.47.4 1.47.5 1.48.0 1.48.1 1.49.0 1.5.0 1.5.0.1 1.5.1 1.5.2 1.5.2.1 1.5.4 1.5.4.1 1.5.5 1.50.0 1.51.0 1.51.1 1.51.2 1.51.3 1.52.0 1.52.1 1.52.2 1.52.3 1.52.4 1.53.0 1.53.1 1.53.2 1.54.0 1.54.1 1.55.0 1.56.0 1.56.1 1.56.2 1.56.3 1.56.4 1.6 1.6.1 1.6.10 1.6.10.1 1.6.10.2 1.6.11 1.6.11.1 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.17.1 1.6.17.2 1.6.2 1.6.2.1 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.6.1 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.6.8.3 1.6.9 1.6.9.1 1.6.9.2 1.6.9.3 1.6.9.4 1.7 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.1.1 1.7.1.2 1.7.1.3 1.7.1.4 1.7.1.5 1.7.10 trunk 1.7.11 1.0.1 1.7.12 1.0.2 1.7.13 1.0.3 1.7.14 1.1.0 1.7.15 1.1.1 1.7.16 1.1.2 1.7.17 1.1.3 1.7.18 1.10 1.7.19 1.10.1 1.7.2 1.10.10 1.7.2.1 1.10.11 1.7.20 1.10.12 1.7.21 1.10.2 1.7.22 1.10.3 1.7.23 1.10.4 1.7.24 1.10.5 1.7.25 1.10.6 1.7.3 1.10.7 1.7.4 1.10.8 1.7.4.1 1.10.9 1.7.4.2 1.11 1.7.4.3 1.11.1 1.7.4.4 1.11.2 1.7.4.5 1.12 1.7.5 1.13 1.7.5.1 1.13.1 1.7.6 1.13.2 1.7.7 1.13.3 1.7.8 1.13.4 1.7.9 1.13.5 1.7.9.1 1.13.6 1.7.9.2 1.13.7 1.7.9.3 1.13.8 1.8 1.14 1.8.1 1.14.1 1.8.10 1.14.10 1.8.11 1.14.11 1.8.12 1.14.2 1.8.13 1.14.3 1.8.14 1.14.4 1.8.15 1.14.5 1.8.16 1.14.6 1.8.17 1.14.7 1.8.18 1.14.8 1.8.19 1.14.9 1.8.2 1.15 1.8.20 1.16 1.8.21 1.16.1 1.8.22 1.17 1.8.23 1.17.1 1.8.24 1.17.10 1.8.25 1.17.10-rc.1 1.8.26 1.17.11 1.8.27 1.17.12 1.8.28 1.17.12-rc.1 1.8.29 1.17.2 1.8.3 1.17.3 1.8.30 1.17.4 1.8.4 1.17.5 1.8.5 1.17.6 1.8.6 1.17.7 1.8.7 1.17.8 1.8.8 1.17.9 1.8.9 1.17.9-beta.1 1.9 1.18.0 2.0.0 1.19.0 2.0.1 1.19.1 2.0.10 1.2 2.0.11 1.2.1 2.0.12 1.2.2 2.0.13 1.2.3 2.0.14 1.2.4 2.0.15 1.2.5 2.0.16 1.2.6 2.0.17 1.2.7 2.0.18 1.20.0 2.0.19 1.20.0-rc.1 2.0.2 1.20.0-rc.2 2.0.20 1.20.1 2.0.3 1.20.2 2.0.4 1.20.3 2.0.5 1.21.0 2.0.6 1.21.1 2.0.7 1.22.0 2.0.8 1.22.1 2.0.9 1.22.2 1.23.0 1.23.1 1.23.2 1.24.0 1.24.1 1.24.2 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.29.0 1.29.1 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.30.0 1.30.1 1.30.2 1.30.2-rc.1 1.30.3 1.30.4 1.30.4-rc.1 1.30.5 1.31.0 1.31.1 1.32.0 1.32.0-rc.1 1.33.0 1.33.1 1.33.2 1.34.0 1.35.0 1.35.1 1.36.0 1.36.1 1.36.2 1.36.3 1.37.0 1.37.1 1.37.2
advanced-ads / src / admin / hooks / useSearchCombobox.js
advanced-ads / src / admin / hooks Last commit date
useFetch.js 2 days ago useFileUploader.js 2 days ago useModalState.js 2 days ago useSearchCombobox.js 2 days ago
useSearchCombobox.js
128 lines
1 /**
2 * External Dependencies
3 */
4 import { useDebounce } from '@wordpress/compose';
5 import { useState, useRef, useCallback, useEffect } from '@wordpress/element';
6
7 export function useSearchCombobox( { endpoint, onSelect, delay = 300 } ) {
8 const [ suggestions, setSuggestions ] = useState( [] );
9 const [ isOpen, setIsOpen ] = useState( false );
10 const [ activeIndex, setActiveIndex ] = useState( -1 );
11 const inputRef = useRef( null );
12
13 const fetchData = useCallback(
14 async ( query ) => {
15 try {
16 const res = await fetch(
17 endpoint.replace( '{{search}}', query )
18 );
19 const data = await res.json();
20 setSuggestions( data );
21 setIsOpen( data.length > 0 );
22 setActiveIndex( -1 );
23 } catch ( err ) {
24 console.error( 'useSearchCombobox fetch error:', err );
25 setSuggestions( [] );
26 }
27 },
28 [ endpoint ]
29 );
30
31 const debouncedFetch = useDebounce( fetchData, delay );
32
33 const close = useCallback( () => {
34 setSuggestions( [] );
35 setIsOpen( false );
36 setActiveIndex( -1 );
37 }, [] );
38
39 const handleInputChange = useCallback(
40 ( e ) => {
41 const query = e.target.value;
42 if ( ! query.trim() ) {
43 close();
44 return;
45 }
46 debouncedFetch( query );
47 },
48 [ debouncedFetch, close ]
49 );
50
51 const handleKeyDown = useCallback(
52 ( e ) => {
53 if ( ! isOpen ) {
54 return;
55 }
56
57 if ( e.key === 'ArrowDown' ) {
58 e.preventDefault();
59 setActiveIndex( ( i ) => ( i + 1 ) % suggestions.length );
60 } else if ( e.key === 'ArrowUp' ) {
61 e.preventDefault();
62 setActiveIndex(
63 ( i ) => ( i - 1 + suggestions.length ) % suggestions.length
64 );
65 } else if ( e.key === 'Enter' ) {
66 e.preventDefault();
67 if ( activeIndex > -1 ) {
68 onSelect( suggestions[ activeIndex ], inputRef );
69 close();
70 }
71 } else if ( e.key === 'Escape' ) {
72 if ( inputRef.current ) {
73 inputRef.current.value = '';
74 }
75 close();
76 }
77 },
78 [ isOpen, suggestions, activeIndex, onSelect, close ]
79 );
80
81 const handleSelect = useCallback(
82 ( suggestion ) => {
83 onSelect( suggestion, inputRef );
84 close();
85 },
86 [ onSelect, close ]
87 );
88
89 useEffect( () => {
90 const onKeyDown = ( e ) => {
91 if ( e.key === 'Escape' && isOpen ) {
92 if ( inputRef.current ) {
93 inputRef.current.value = '';
94 }
95 close();
96 }
97 };
98 document.addEventListener( 'keydown', onKeyDown );
99 return () => document.removeEventListener( 'keydown', onKeyDown );
100 }, [ isOpen, close ] );
101
102 // Spread these directly onto your <input>
103 const inputProps = {
104 ref: inputRef,
105 role: 'combobox',
106 'aria-autocomplete': 'list',
107 'aria-expanded': isOpen,
108 'aria-activedescendant':
109 activeIndex > -1 ? `suggestion-${ activeIndex }` : undefined,
110 onChange: handleInputChange,
111 onKeyDown: handleKeyDown,
112 };
113
114 // Spread these directly onto your <ul>
115 const listProps = {
116 role: 'listbox',
117 };
118
119 return {
120 suggestions,
121 isOpen,
122 activeIndex,
123 inputProps,
124 listProps,
125 handleSelect,
126 };
127 }
128