PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / trunk
GiveWP – Donation Plugin and Fundraising Platform vtrunk
4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / src / Admin / hooks / useAsyncSelectOption.ts
give / src / Admin / hooks Last commit date
useAsyncSelectOption.ts 8 months ago
useAsyncSelectOption.ts
132 lines
1 import {useCallback, useEffect, useState} from 'react';
2 import apiFetch from '@wordpress/api-fetch';
3 import {UseAsyncSelectOptionReturn} from '@givewp/admin/types';
4
5 /**
6 * Custom hook for handling async option selection with pagination and search
7 *
8 * @since 4.11.0
9 */
10 export function useAsyncSelectOptions({
11 recordId,
12 selectedOptionRecord,
13 endpoint,
14 recordsFormatter = (records: any) => records,
15 optionFormatter,
16 queryParams,
17 perPage = 30,
18 resetOnChange = false,
19 }: AsyncSelectOptionsConfig): UseAsyncSelectOptionReturn {
20 const [page, setPage] = useState(0);
21 const [selectedOption, setSelectedOption] = useState<Option | null>(null);
22 const [error, setError] = useState<Error | null>(null);
23
24 // Reset page when reset property changes
25 useEffect(() => {
26 if (resetOnChange !== undefined) {
27 setPage(0);
28 }
29 }, [resetOnChange]);
30
31 useEffect(() => {
32 if (selectedOptionRecord && recordId) {
33 setSelectedOption(optionFormatter(selectedOptionRecord));
34 } else if (!recordId) {
35 setSelectedOption(null);
36 }
37 }, [selectedOptionRecord, recordId]);
38
39 // Load options function for AsyncPaginate
40 const loadOptions = useCallback(async (searchInput: string) => {
41 const currentPage = searchInput ? 1 : page + 1;
42
43 const params = new URLSearchParams({
44 ...queryParams,
45 per_page: perPage.toString(),
46 page: currentPage.toString(),
47 ...(searchInput && {search: searchInput}),
48 });
49
50 setError(null);
51
52 try {
53 const records = recordsFormatter(await apiFetch<[]>({
54 path: `${endpoint}?${params.toString()}`,
55 }));
56
57 const newOptions = (records || []).map(optionFormatter);
58
59 // Update page state
60 if (searchInput !== '') {
61 setPage(1);
62 } else if (!searchInput) {
63 setPage(currentPage);
64 }
65
66 const hasMoreResults = (records?.length || 0) >= perPage;
67
68 return {
69 options: newOptions,
70 hasMore: hasMoreResults,
71 };
72 } catch (err) {
73 const loadError = err instanceof Error ? err : new Error(`Failed to load options`);
74 setError(loadError);
75 console.error(`Failed to load options`, loadError);
76
77 return {
78 options: [],
79 hasMore: false,
80 };
81 }
82 }, [page, JSON.stringify(queryParams)]);
83
84 // Map options for menu (deduplication and ordering)
85 const mapOptionsForMenu = useCallback(
86 (options: Option[]) => filterOptionsForSelect(options, selectedOption),
87 [selectedOption],
88 );
89
90 return {
91 selectedOption,
92 loadOptions,
93 mapOptionsForMenu,
94 error,
95 };
96 }
97
98 export type Option = {
99 value: number;
100 label: string;
101 }
102
103 export type AsyncSelectOptionsConfig = {
104 recordId: number | null;
105 selectedOptionRecord: any;
106 recordsFormatter?: (records: any) => any;
107 optionFormatter: (record: any) => Option;
108 endpoint: string;
109 queryParams: {};
110 perPage?: number;
111 resetOnChange?: any;
112 }
113
114 export function filterOptionsForSelect(options: Option[], selectedOption: Option | null): Option[] {
115 // Remove duplicates and sort alphabetically
116 const filteredOptions = options
117 .filter((option, index, self) => index === self.findIndex((t) => t.value === option.value))
118 .sort((a, b) => a.label.localeCompare(b.label));
119
120 // If no selected option, return filtered list
121 if (!selectedOption) {
122 return filteredOptions;
123 }
124
125 // Put selected option first, then other options (excluding the selected one)
126 return [
127 selectedOption,
128 ...filteredOptions.filter(option => option.value !== selectedOption.value),
129 ];
130 }
131
132