import { useMemo, useRef, useState } from '@wordpress/element'; import { BaseControl, Button, MenuGroup, MenuItem, NavigableMenu, Popover, SearchControl, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import type { CurrencyEntry } from '../../currencies'; type CurrencyRichSelectProps = { label: string; value: string; currencies: CurrencyEntry[]; onChange: (code: string) => void; help?: string; compact?: boolean; availableCodes?: Set; }; type CurrencyMultiRichSelectProps = { label: string; values: string[]; currencies: CurrencyEntry[]; onChange: (codes: string[]) => void; help?: string; availableCodes?: Set; }; type GroupedCurrencies = { group: string; label: string; items: CurrencyEntry[]; }; function useGroupedCurrencies( currencies: CurrencyEntry[], query: string, groupOrder: string[], groupLabels: Record ): GroupedCurrencies[] { const filtered = useMemo(() => { const normalizedQuery = query.trim().toLowerCase(); if (normalizedQuery === '') { return currencies; } return currencies.filter((currency) => { return ( currency.code.toLowerCase().includes(normalizedQuery) || currency.name.toLowerCase().includes(normalizedQuery) ); }); }, [currencies, query]); return useMemo(() => { const map = new Map(); for (const currency of filtered) { const list = map.get(currency.group) ?? []; list.push(currency); map.set(currency.group, list); } const orderedKeys: string[] = []; for (const key of groupOrder) { if (map.has(key)) { orderedKeys.push(key); } } for (const key of map.keys()) { if (!orderedKeys.includes(key)) { orderedKeys.push(key); } } return orderedKeys.map((group) => ({ group, label: groupLabels[group] || group, items: map.get(group) ?? [], })); }, [filtered, groupLabels, groupOrder]); } export function CurrencyRichSelect({ label, value, currencies, onChange, help, compact, availableCodes, }: CurrencyRichSelectProps) { const [isOpen, setIsOpen] = useState(false); const [query, setQuery] = useState(''); const buttonRef = useRef(null); const ui = window.crtodayEditorCurrenciesUi ?? {}; const groupLabels = ui.groupLabels ?? {}; const groupOrder = ui.groupOrder ?? []; const flagBaseUrl = ui.flagBaseUrl ?? ''; const labels = ui.labels ?? {}; const selected = useMemo( () => currencies.find((currency) => currency.code === value), [currencies, value] ); const grouped = useGroupedCurrencies(currencies, query, groupOrder, groupLabels); const selectedLabel = selected ? (compact ? selected.code : `${selected.code} — ${selected.name}`) : value; return ( {isOpen && buttonRef.current ? ( setIsOpen(false)} placement="bottom-start" focusOnMount shift >
setQuery(next)} placeholder={labels.searchCurrency ?? ''} label={labels.searchCurrency ?? ''} hideLabelFromVision />
{grouped.length === 0 ? (
{labels.noResults ?? ''}
) : ( {grouped.map((group) => ( {group.items.map((currency) => { const isSelected = currency.code === value; const isUnavailable = availableCodes != null && !availableCodes.has(currency.code); return ( { if (isUnavailable) return; onChange(currency.code); setIsOpen(false); setQuery(''); }} > {currency.flag && flagBaseUrl ? ( ) : null} {currency.code} {currency.name} {isSelected ? : null} ); })} ))} )}
) : null}
); } export function CurrencyMultiRichSelect({ label, values, currencies, onChange, help, availableCodes, }: CurrencyMultiRichSelectProps) { const [isOpen, setIsOpen] = useState(false); const [query, setQuery] = useState(''); const buttonRef = useRef(null); const ui = window.crtodayEditorCurrenciesUi ?? {}; const groupLabels = ui.groupLabels ?? {}; const groupOrder = ui.groupOrder ?? []; const flagBaseUrl = ui.flagBaseUrl ?? ''; const labels = ui.labels ?? {}; const grouped = useGroupedCurrencies(currencies, query, groupOrder, groupLabels); const selectedSet = useMemo(() => new Set(values), [values]); const selectedLabel = useMemo(() => { if (values.length === 0) { return labels.noCurrenciesSelected ?? ''; } if (values.length <= 3) { return values.join(', '); } return (labels.selectedCurrenciesPattern ?? '').replace('%d', String(values.length)); }, [labels.noCurrenciesSelected, labels.selectedCurrenciesPattern, values]); const toggleCurrency = (code: string) => { if (selectedSet.has(code)) { onChange(values.filter((item) => item !== code)); return; } onChange([...values, code]); }; return ( {isOpen && buttonRef.current ? ( setIsOpen(false)} placement="bottom-start" focusOnMount shift >
setQuery(next)} placeholder={labels.searchCurrency ?? ''} label={labels.searchCurrency ?? ''} hideLabelFromVision />
{grouped.length === 0 ? (
{labels.noResults ?? ''}
) : ( {grouped.map((group) => ( {group.items.map((currency) => { const isSelected = selectedSet.has(currency.code); const isUnavailable = availableCodes != null && !availableCodes.has(currency.code); return ( { if (isUnavailable && !isSelected) return; toggleCurrency(currency.code); }} > {currency.flag && flagBaseUrl ? ( ) : null} {currency.code} {currency.name} ); })} ))} )}
) : null}
); }