Files
reflector/www/app/components/SearchableLanguageSelect.tsx
Sergey Mankovsky 0931095f49 fix: remaining dependabot security issues (#890)
* Upgrade docs deps

* Upgrade frontend to latest deps

* Update package overrides

* Remove redundant deps

* Add tailwind postcss plugin

* Replace language select with chakra

* Fix main nav

* Patch gray matter

* Fix webpack override

* Replace python-jose with pyjwt

* Override kv url for frontend in compose

* Upgrade hatchet sdk

* Update docs

* Supress pydantic warnings
2026-03-02 17:17:40 +01:00

99 lines
2.6 KiB
TypeScript

"use client";
import React, { useMemo } from "react";
import {
Combobox,
createListCollection,
useComboboxContext,
} from "@chakra-ui/react";
export type LangOption = { value: string | undefined; name: string };
type Item = { label: string; value: string };
function FilteredComboboxItems({ items }: { items: Item[] }) {
const ctx = useComboboxContext();
const inputValue = (ctx as { inputValue?: string }).inputValue ?? "";
const filtered = useMemo(() => {
const q = inputValue.trim().toLowerCase();
if (!q) return items;
return items.filter((item) => item.label.toLowerCase().includes(q));
}, [items, inputValue]);
return (
<>
<Combobox.Empty>No matches</Combobox.Empty>
{filtered.map((item) => (
<Combobox.Item key={item.value} item={item}>
{item.label}
</Combobox.Item>
))}
</>
);
}
type Props = {
options: LangOption[];
value: string;
onChange: (value: string) => void;
placeholder: string;
};
export function SearchableLanguageSelect({
options,
value,
onChange,
placeholder,
}: Props) {
const items = useMemo(() => {
const result: Item[] = [];
let addedNone = false;
for (const opt of options) {
const val = opt.value ?? "NOTRANSLATION";
if (val === "NOTRANSLATION" || val === "") {
if (addedNone) continue;
addedNone = true;
result.push({ label: "No translation", value: "NOTRANSLATION" });
} else {
result.push({ label: opt.name, value: val });
}
}
return result.sort((a, b) => {
if (a.value === "NOTRANSLATION") return -1;
if (b.value === "NOTRANSLATION") return 1;
return a.label.localeCompare(b.label);
});
}, [options]);
const collection = useMemo(() => createListCollection({ items }), [items]);
const selectedValues = value && value !== "NOTRANSLATION" ? [value] : [];
return (
<Combobox.Root
collection={collection}
value={selectedValues}
onValueChange={(e) => onChange(e.value[0] ?? "NOTRANSLATION")}
openOnClick
closeOnSelect
selectionBehavior="replace"
placeholder={placeholder}
className="form-combobox"
size="md"
positioning={{ strategy: "fixed", hideWhenDetached: true }}
>
<Combobox.Control>
<Combobox.Input />
<Combobox.IndicatorGroup>
<Combobox.Trigger />
</Combobox.IndicatorGroup>
</Combobox.Control>
<Combobox.Positioner>
<Combobox.Content>
<FilteredComboboxItems items={items} />
</Combobox.Content>
</Combobox.Positioner>
</Combobox.Root>
);
}