Neovici/cosmoz-autocomplete

View on GitHub
src/listbox/use-listbox.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { useMemo } from '@pionjs/pion';
import { useHost } from '@neovici/cosmoz-utils/hooks/use-host';
import { usePosition, Placement } from '@neovici/cosmoz-dropdown/use-position';
import { byValue } from './util';
import { useItems } from './use-items';
import { useRenderItem, ItemRenderer } from './use-render-item';

export const properties = [
    'query',
    'items',
    'onSelect',
    'textual',
    'anchor',
    'confinement',
    'placement',
    'itemHeight',
    'itemLimit',
    'itemRenderer',
    'defaultIndex',
    'value',
    'valueProperty',
];

export interface Props<I> {
    value: I | I[];
    valueProperty?: string;
    items: I[];
    onSelect: (i: I) => void;
    defaultIndex?: number;
    query: string;
    textual: (i: I) => string;
    itemRenderer?: ItemRenderer<I>;
    itemHeight?: number;
    itemLimit?: number;
    anchor?: () => HTMLElement | null;
    confinement?: HTMLElement;
    placement?: Placement;
}

export const useListbox = <I>({
    value,
    valueProperty,
    items: _items,
    onSelect,
    defaultIndex,
    query,
    textual,
    itemRenderer,
    itemHeight = 40,
    itemLimit = 5,
    ...thru
}: Props<I>) => {
    const isSelected = useMemo(
            () => byValue(value, valueProperty),
            [value, valueProperty]
        ),
        // TODO: investigate if we can drop this
        items = useMemo(() => _items.slice(), [_items, isSelected]),
        { position, highlight, select } = useItems({
            items,
            onSelect,
            defaultIndex: isNaN(defaultIndex as number)
                ? undefined
                : Number(defaultIndex),
        });

    usePosition({
        host: useHost(),
        ...thru,
        limit: true,
    });

    return {
        position,
        items,
        height: Math.min(itemLimit, items.length) * itemHeight,
        highlight,
        select,
        itemHeight,
        renderItem: useRenderItem({
            itemRenderer,
            highlight,
            select,
            textual,
            query,
            isSelected,
        }),
    };
};

export { useRenderItem };