# All files / src/form useComboboxList.ts

39.62% Statements 42/106
100% Branches 0/0
0% Functions 0/2
39.62% Lines 42/106

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    1071x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x

    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x

     import type { KeyboardEventHandler, Ref, RefCallback, RefObject } from "react";
    import { useEnsuredId } from "../useEnsuredId";
    import { useEnsuredRef } from "../useEnsuredRef";
    import type {
      ComboboxImplementation,
      ComboboxOptions,
      ComboboxWidgetProps,
    } from "./useCombobox";
    import { useCombobox } from "./useCombobox";

    const noop = (): void => {
      // do nothing
    };

    export interface ComboboxListOptions<
      ComboboxEl extends HTMLElement = HTMLInputElement,
      ListboxEl extends HTMLElement = HTMLDivElement
    > extends ComboboxOptions<ComboboxEl> {
      listboxId?: string;
      listboxRef?: Ref<ListboxEl>;
      comboboxRef?: Ref<ComboboxEl>;
      onKeyDown?: KeyboardEventHandler<ComboboxEl>;
    }

    export interface ComboboxListboxProps<E extends HTMLElement = HTMLDivElement> {
      id: string;
      ref: RefCallback<E>;
      role: "listbox";
    }

    export interface ComboboxListImplementation<
      ComboboxEl extends HTMLElement = HTMLInputElement,
      ListboxEl extends HTMLElement = HTMLDivElement
    > extends ComboboxImplementation<ComboboxEl> {
      comboboxRef: RefObject<ComboboxEl>;
      comboboxProps: ComboboxWidgetProps<ComboboxEl> & {
        ref: RefCallback<ComboboxEl>;
        onKeyDown: KeyboardEventHandler<ComboboxEl>;
      };
      listboxRef: RefObject<ListboxEl>;
      listboxProps: ComboboxListboxProps<ListboxEl>;
    }

    export function useComboboxList<
      ComboboxEl extends HTMLElement,
      ListboxEl extends HTMLElement
    >(
      options: ComboboxListOptions<ComboboxEl, ListboxEl> = {}
    ): ComboboxListImplementation<ComboboxEl, ListboxEl> {
      const {
        disabled = false,
        onKeyDown = noop,
        listboxId: propListboxId,
        listboxRef: propListboxRef,
        comboboxRef: propComboboxRef,
      } = options;
      const listboxId = useEnsuredId(propListboxId, "listbox");
      const [comboboxRef, comboboxRefCallback] = useEnsuredRef(propComboboxRef);
      const [listboxRef, listboxRefCallback] = useEnsuredRef(propListboxRef);

      const combobox = useCombobox(options);
      const { hide, visible, comboboxProps } = combobox;

      return {
        ...combobox,
        comboboxRef,
        comboboxProps: {
          ...comboboxProps,
          ref: comboboxRefCallback,
          onKeyDown(event) {
            onKeyDown(event);
            if (disabled) {
              return;
            }

            if (!visible) {
              return;
            }

            switch (event.key) {
              case "Escape":
              case "Tab":
                event.stopPropagation();
                hide();
                break;
              case "Enter":
                event.preventDefault();
                event.stopPropagation();
                // select item
                break;
              case "ArrowDown":
              case "ArrowUp":
                event.preventDefault();
                // move
                break;
            }
          },
        },
        listboxRef,
        listboxProps: {
          id: listboxId,
          ref: listboxRefCallback,
          role: "listbox",
        },
      };
    }