import apiFetch from "@wordpress/api-fetch"; import type { CSSProperties } from "react"; import { useEffect, useMemo, useState } from "@wordpress/element"; import { Button, CheckboxControl, RadioControl, SearchControl, Spinner, } from "@wordpress/components"; import { sprintf, __ } from "@wordpress/i18n"; export type ClassificationTaxonomy = | "destination" | "activity" | "trip_category" | "difficulty"; interface ChoiceItem { id: number; name: string; } type Scope = "all" | "narrow"; interface ClassificationMultiSelectProps { taxonomy: ClassificationTaxonomy; label: string; help: string; value: number[]; onChange: (ids: number[]) => void; } const listWrapStyle: CSSProperties = { maxHeight: 220, overflowY: "auto", marginTop: 8, padding: "4px 0", border: "1px solid #94949451", borderRadius: 2, }; export function ClassificationMultiSelect({ taxonomy, label, help, value, onChange, }: ClassificationMultiSelectProps) { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [loadError, setLoadError] = useState(false); const [search, setSearch] = useState(""); const [scope, setScope] = useState(() => value.length > 0 ? "narrow" : "all", ); useEffect(() => { if (value.length > 0) { setScope("narrow"); } }, [value.length]); useEffect(() => { let cancelled = false; setLoading(true); setLoadError(false); apiFetch<{ items?: ChoiceItem[] }>({ path: `/yatra/v1/block-editor/taxonomy-choices?taxonomy=${encodeURIComponent( taxonomy, )}`, }) .then((response) => { if (!cancelled) { setItems(response.items ?? []); } }) .catch(() => { if (!cancelled) { setItems([]); setLoadError(true); } }) .finally(() => { if (!cancelled) { setLoading(false); } }); return () => { cancelled = true; }; }, [taxonomy]); const filteredItems = useMemo(() => { const q = search.trim().toLowerCase(); if (q === "") { return items; } return items.filter((i) => i.name.toLowerCase().includes(q)); }, [items, search]); const selectedSet = useMemo(() => new Set(value), [value]); const toggleId = (id: number, checked: boolean) => { const next = new Set(selectedSet); if (checked) { next.add(id); } else { next.delete(id); } onChange([...next].sort((a, b) => a - b)); }; const onScopeChange = (next: string) => { if (next === "all") { setScope("all"); onChange([]); setSearch(""); } else { setScope("narrow"); } }; if (loading) { return (
{label}
); } return (
{label}

{help}

{loadError && (

{__( "Could not load options. Confirm you can edit posts and Yatra REST is available.", "yatra", )}

)} {scope === "all" && (

{__( "The frontend will include every matching published item.", "yatra", )}

)} {scope === "narrow" && ( <> setSearch(s)} __nextHasNoMarginBottom /> {value.length > 0 && (

{sprintf( /* translators: %d = number of selected taxonomy items */ __("%d selected", "yatra"), value.length, )}

)} {items.length === 0 && !loadError ? (

{__("No published items of this type yet.", "yatra")}

) : (
{filteredItems.length === 0 ? (

{__("No matching items.", "yatra")}

) : ( filteredItems.map((item) => { const cid = Number(item.id); return (
toggleId(cid, checked === true)} __nextHasNoMarginBottom />
); }) )}
)} {value.length > 0 && ( )}

{__( "If none are checked, the block behaves like “All” (no taxonomy filter).", "yatra", )}

)}
); }