SearchInput
ui forminputsearch
Suchfeld mit führendem Lupen-Icon und type="search". Kontrolliert über value/onChange(string). Einheitliche Control-Höhe.
Barrierefreiheit
- type="search"; bei eigenständigem Einsatz ein aria-label/Placeholder vergeben.
Beispiele
Kontrolliert
const [q, setQ] = useState("");
<SearchInput value={q} onChange={setQ} placeholder="Suchen…" /> Genutzte Tokens · 11
--bg--border--control-height--fg--field-font-size--field-pad-x--field-radius--font-mono--muted--surface--text-xs
Quelldateien
components/ui/SearchInput.tsx tsx
import type { ReactNode, Ref } from 'react';
import { SearchIcon } from './Icon.tsx';
import styles from './SearchInput.module.css';
/**
* Suchfeld mit vorangestelltem Icon (1:1 aus media-presenter
* `components/SearchInput.tsx`). Store-frei: `value`/`onChange` sind die
* Softkey-Hooks, die Konsumenten verdrahten (presenter: Global-Search-Store;
* worship: Lied-Suche gegen den DataProvider).
*/
export interface SearchInputProps {
value: string;
onChange: (value: string) => void;
placeholder?: string;
iconSize?: number;
autoFocus?: boolean;
className?: string;
/** Ref auf das Eingabefeld — z. B. für einen globalen ⌘K-Fokus-Shortcut. */
inputRef?: Ref<HTMLInputElement>;
/** Rechts im Feld eingeblendet, z. B. der Shortcut-Hinweis „⌘ K". */
trailing?: ReactNode;
}
export function SearchInput({
value,
onChange,
placeholder,
iconSize = 14,
autoFocus,
className,
inputRef,
trailing,
}: SearchInputProps) {
const wrapperClass = [styles.wrap, className].filter(Boolean).join(' ');
const inputClass = [styles.input, trailing ? styles.hasTrailing : ''].filter(Boolean).join(' ');
return (
<div className={wrapperClass}>
<span className={styles.icon}>
<SearchIcon size={iconSize} />
</span>
<input
ref={inputRef}
type="search"
className={inputClass}
placeholder={placeholder}
value={value}
onChange={(e) => onChange(e.target.value)}
autoFocus={autoFocus}
/>
{trailing && <span className={styles.trailing}>{trailing}</span>}
</div>
);
}
components/ui/SearchInput.module.css css
.wrap {
position: relative;
}
.input {
width: 100%;
height: var(--control-height);
padding: 0 var(--field-pad-x) 0 32px;
border-radius: var(--field-radius);
border: 1px solid var(--border);
background: var(--bg);
color: var(--fg);
font-size: var(--field-font-size);
line-height: 1;
box-sizing: border-box;
}
.icon {
position: absolute;
left: 8px;
top: 50%;
transform: translateY(-50%);
color: var(--muted);
display: inline-flex;
}
/* Platz für den Trailing-Slot (Shortcut-Hinweis) freihalten. */
.hasTrailing {
padding-right: 52px;
}
/* Trailing-Slot rechts im Feld — Optik des presenter-⌘K-Hinweises. */
.trailing {
position: absolute;
right: 4px;
top: 50%;
transform: translateY(-50%);
font-family: var(--font-mono);
font-size: var(--text-xs);
color: var(--muted);
padding: 2px 6px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--surface);
pointer-events: none;
}
Holen
# MCP (Claude Code / Cursor)
get_component({ name: "search-input" })
# Registry direkt
https://www.ekklesi.tools/design/registry/components/search-input.json