import { Box, Text, useUiHost } from "../../ui"; import { t } from "../../i18n"; import { type AlertContext, useDialogKeyboard } from "../../ui/dialog"; import { useEffect, useRef, useState } from "react"; import type { PaneSettingField, PaneSettingTextField, } from "../../types/plugin"; import { colors } from "../../theme/colors"; import { Button, DialogFrame, MultiSelectDialogContent, TextField } from "../ui"; import { listCursorMove } from "../ui/list-view"; import { DesktopDialogSurface, desktopText } from "./desktop"; import { TuiDialogList, useTuiDialogListRows } from "./tui"; import { coerceSelectedPaneSettingValues, isSpaceKey } from "./value"; type SelectPaneSettingField = Extract; type MultiSelectPaneSettingField = Extract; interface SelectFieldDialogProps extends AlertContext { field: SelectPaneSettingField; currentValue: unknown; onApply: (value: string) => Promise; } interface TextFieldDialogProps extends AlertContext { field: PaneSettingTextField; currentValue: unknown; onApply: (value: string) => Promise; } function useSelectFieldDialogController({ dismiss, field, currentValue, onApply, }: SelectFieldDialogProps) { const initialIndex = Math.max(0, field.options.findIndex((option) => option.value === currentValue)); const [selectedIndex, setSelectedIndex] = useState(initialIndex); const pageSize = useTuiDialogListRows() - 1; const applyOption = (value: string) => { void onApply(value).then(() => dismiss()).catch(() => {}); }; useDialogKeyboard((event) => { event.stopPropagation(); const move = listCursorMove(event, pageSize); if (move) setSelectedIndex((index) => move(field.options, index)); else if (event.name === "escape") dismiss(); else if (event.name === "enter" || event.name === "return" || isSpaceKey(event)) { const option = field.options[selectedIndex]; if (!option) return; applyOption(option.value); } }); return { selectedIndex, setSelectedIndex, applyOption }; } export function TuiSelectFieldDialog(props: SelectFieldDialogProps) { const { field } = props; const { selectedIndex, setSelectedIndex, applyOption } = useSelectFieldDialogController(props); return ( ({ id: option.value, label: option.label, description: option.description, }))} selectedIndex={selectedIndex} bgColor={colors.commandBg} onSelect={setSelectedIndex} onActivate={(item) => { applyOption(item.id); }} /> ); } function useTextFieldDialogController({ dismiss, currentValue, onApply, }: TextFieldDialogProps) { const [value, setValue] = useState(typeof currentValue === "string" ? currentValue : ""); const [error, setError] = useState(null); const inputRef = useRef(null); useEffect(() => { inputRef.current?.focus?.(); }, []); useDialogKeyboard((event) => { if (event.name === "escape") { event.stopPropagation(); dismiss(); } }, { allowEditable: true }); const submit = async () => { try { setError(null); await onApply(value); dismiss(); } catch (err) { setError(err instanceof Error ? err.message : t("Could not save this value.")); } }; return { value, setValue, error, inputRef, submit }; } export function TextFieldDialog(props: TextFieldDialogProps) { return useUiHost().kind === "desktop-web" ? : ; } function DesktopTextFieldDialog(props: TextFieldDialogProps) { const { dismiss, field } = props; const { value, setValue, error, inputRef, submit } = useTextFieldDialogController(props); return ( { void submit(); }} /> {error && ( {error} )}