import { For, createSignal, onMount, onCleanup } from 'solid-js'; import { defineWebComponent } from './define'; import { PromptSuggestion } from '../components/prompt-suggestion'; type Item = string | { label: string; value?: string }; interface Props extends Record { /** The suggestions. Strings, or `{ label, value }` when the displayed text * and the emitted value differ. Set as a JS property. */ suggestions: Item[]; /** Chip style: `'outline'` (default), `'ghost'`, or `'default'` (filled). */ variant?: 'outline' | 'ghost' | 'default'; /** Size preset for each chip. Defaults to the pill default (`'lg'`); pass * `'sm'` for smaller pills (or `'md'`). */ size?: 'sm' | 'md' | 'lg' | 'icon' | 'icon-sm'; /** Full-width left-aligned rows instead of pills. */ block?: boolean; /** Substring to highlight within each suggestion. */ highlight?: string; } /** Events fired by ``. */ interface Events { /** A suggestion was clicked. */ 'kc-select': { value: string }; } const labelOf = (s: Item) => (typeof s === 'string' ? s : s.label); const valueOf = (s: Item) => (typeof s === 'string' ? s : s.value ?? s.label); /** Parse a single `` node into an `Item` descriptor. */ export function parseSuggestionNode(n: Element): Item { const text = n.textContent?.trim() ?? ''; const value = n.getAttribute('value') ?? text; return { label: text, value }; } /** * `` — a row/list of suggestion chips. Data via the * `suggestions` property; `variant`/`block`/`highlight` attributes; emits * `select`. * * Alternatively, declare chips as `` child elements * (light-DOM data carriers — hidden by the Shadow DOM): * * ```html * * Use Vue * Use React * * ``` */ defineWebComponent('kc-suggestions', { suggestions: [], variant: 'outline', size: undefined, block: false, highlight: undefined, }, (props, { dispatch, flag, element }) => { // Read declarative children from light DOM. // Shadow DOM with no suppresses them visually — they're invisible data carriers. const [slottedSuggestions, setSlottedSuggestions] = createSignal([]); onMount(() => { const read = () => { const nodes = [...element.querySelectorAll('kc-suggestion')]; setSlottedSuggestions(nodes.map(parseSuggestionNode)); }; read(); const observer = new MutationObserver(read); observer.observe(element, { childList: true, attributes: true, subtree: true }); onCleanup(() => observer.disconnect()); }); // Merge prop suggestions (first) with declarative children (after). const allSuggestions = () => [...(props.suggestions ?? []), ...slottedSuggestions()]; return (
{(s) => ( dispatch('kc-select', { value: valueOf(s) })} > {labelOf(s)} )}
); });