# All files / src/form MenuItemFileInput.tsx

0% Statements 0/113
0% Branches 0/1
0% Functions 0/1
0% Lines 0/113

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
    107
    108
    109
    110
    111
    112
    113
    114

     "use client";
    import {
      forwardRef,
      type ChangeEventHandler,
      type InputHTMLAttributes,
    } from "react";
    import { getIcon } from "../icon/iconConfig.js";
    import { MenuItem, type MenuItemProps } from "../menu/MenuItem.js";
    import { useEnsuredId } from "../useEnsuredId.js";

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

    /**
     * @remarks \@since 6.0.0 Removed most of the shared `FileInputProps` from this
     * implementation.
     */
    export interface MenuItemFileInputProps
      extends Omit<MenuItemProps, "onChange"> {
      /**
       * A change event handler that should do something with the selected files.
       * Usually the `onChange` returned by `useFileUpload` or:
       *
       * ```ts
       * onChange={(event) => {
       *   const { files } = event.currentTarget;
       *   // do something with files
       * }}
       * ```
       *
       * @remarks
       * This is actually a native `Event` and not a `SyntheticEvent` because the
       * file input is created through `document.createElement` instead of `React`.
       * You can still access the files through `event.currentTarget.files` like
       * normal.
       */
      onChange: ChangeEventHandler<HTMLInputElement>;

      accept?: InputHTMLAttributes<HTMLInputElement>["accept"];
      capture?: InputHTMLAttributes<HTMLInputElement>["capture"];
      multiple?: InputHTMLAttributes<HTMLInputElement>["multiple"];

      /**
       * Set this to `true` if the `Menu` should not close when the file input's
       * menu item is clicked.
       *
       * @defaultValue `false`
       */
      preventMenuHideOnClick?: boolean;
    }

    /**
     * **Client Component**
     *
     * A wrapper for the `<input type="file">` element that works within menus.
     *
     * @remarks
     * \@since 5.0.0
     * \@since 6.0.0 No longer creates an invisible file input element within the
     * menu item. This allows the menu to be closed immediately when this menu item
     * is clicked.
     */
    export const MenuItemFileInput = forwardRef<
      HTMLLIElement,
      MenuItemFileInputProps
    >(function MenuItemFileInput(props, ref) {
      const {
        id: propId,
        onClick = noop,
        onChange,
        accept = "",
        multiple = false,
        children,
        leftAddon: propLeftAddon,
        preventMenuHideOnClick = false,
        ...remaining
      } = props;

      const id = useEnsuredId(propId, "menu-item");
      const leftAddon = getIcon("upload", propLeftAddon);

      return (
        <MenuItem
          {...remaining}
          id={id}
          ref={ref}
          leftAddon={leftAddon}
          onClick={(event) => {
            onClick(event);

            if (preventMenuHideOnClick) {
              event.stopPropagation();
            }

            // create a temporary file input element and click it to trigger the
            // file upload behavior.
            let input: HTMLInputElement | null = document.createElement("input");
            input.type = "file";
            input.accept = accept;
            input.multiple = multiple;

            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-expect-error
            input.onchange = onChange;
            input.click();
            input = null;
          }}
        >
          {children}
        </MenuItem>
      );
    });