ultisuite-client/components/gmail/settings/automation/automation-suggest-input.tsx
2026-05-25 13:52:40 +02:00

117 lines
3.6 KiB
TypeScript

'use client'
import { createContext, useContext, useMemo, useState } from 'react'
import { Input } from '@/components/ui/input'
import { cn } from '@/lib/utils'
import {
useAutomationSuggestions,
type AutomationSuggestion,
} from '@/lib/mail-automation/use-automation-suggestions'
import type { AutomationSuggestionKind } from '@/lib/mail-automation/condition-helpers'
interface AutomationSuggestionsContextValue {
suggestionsFor: (kind: AutomationSuggestionKind) => AutomationSuggestion[]
}
const AutomationSuggestionsContext = createContext<AutomationSuggestionsContextValue | null>(null)
export function AutomationSuggestionsProvider({ children }: { children: React.ReactNode }) {
const { suggestionsFor } = useAutomationSuggestions()
const value = useMemo(() => ({ suggestionsFor }), [suggestionsFor])
return (
<AutomationSuggestionsContext.Provider value={value}>
{children}
</AutomationSuggestionsContext.Provider>
)
}
function useAutomationSuggestionsContext() {
return useContext(AutomationSuggestionsContext)
}
interface AutomationSuggestInputProps {
kind: AutomationSuggestionKind
value: string
onChange: (value: string) => void
placeholder?: string
className?: string
disabled?: boolean
}
export function AutomationSuggestInput({
kind,
value,
onChange,
placeholder,
className,
disabled,
}: AutomationSuggestInputProps) {
const ctx = useAutomationSuggestionsContext()
const [listId] = useState(() => `automation-suggest-${kind}-${Math.random().toString(36).slice(2, 9)}`)
const suggestions = kind === 'none' || !ctx ? [] : ctx.suggestionsFor(kind)
const filtered = useMemo(() => {
if (!value.trim()) return suggestions.slice(0, 40)
const q = value.trim().toLowerCase()
return suggestions
.filter(
(s) =>
s.value.toLowerCase().includes(q) ||
(s.label?.toLowerCase().includes(q) ?? false)
)
.slice(0, 40)
}, [suggestions, value])
if (kind === 'none' || suggestions.length === 0) {
return (
<Input
className={cn('h-8 text-xs', className)}
value={value}
placeholder={placeholder}
disabled={disabled}
onChange={(e) => onChange(e.target.value)}
/>
)
}
return (
<div className="relative">
<Input
className={cn('h-8 text-xs', className)}
value={value}
placeholder={placeholder}
disabled={disabled}
list={listId}
onChange={(e) => onChange(e.target.value)}
/>
<datalist id={listId}>
{filtered.map((s) => (
<option key={`${s.value}-${s.label ?? ''}`} value={s.value}>
{s.label && s.label !== s.value ? s.label : undefined}
</option>
))}
</datalist>
{filtered.length > 0 && value.trim() && !disabled ? (
<ul className="absolute z-20 mt-1 max-h-40 w-full overflow-y-auto rounded-md border border-border bg-popover py-1 shadow-md">
{filtered.slice(0, 8).map((s) => (
<li key={`pick-${s.value}-${s.label ?? ''}`}>
<button
type="button"
className="w-full px-2 py-1.5 text-left text-xs hover:bg-muted"
onMouseDown={(e) => {
e.preventDefault()
onChange(s.value)
}}
>
<span className="block truncate">{s.label ?? s.value}</span>
{s.label && s.label !== s.value ? (
<span className="block truncate font-mono text-[10px] text-muted-foreground">{s.value}</span>
) : null}
</button>
</li>
))}
</ul>
) : null}
</div>
)
}