import { defineComponent, mergeModels, useSlots, useModel, useAttrs, computed, useTemplateRef, ref, onMounted, nextTick, watch, createElementBlock, openBlock, mergeProps, createCommentVNode, unref, normalizeClass, renderSlot, createTextVNode, toDisplayString, createElementVNode, withModifiers } from "vue";
import { u as useFileDialog, b as useDropZone } from "./index-nMtnPEP0.mjs";
import { u as useDefaults } from "./useDefaults-DvqfKEoD.mjs";
import { u as useId } from "./useId-9_HUwM96.mjs";
import { u as useStateClass } from "./useStateClass-BGbSLWFN.mjs";
import { i as isEmptySlot } from "./dom-DVb-f0ZG.mjs";
const _hoisted_1 = ["for"];
const _hoisted_2 = ["aria-disabled"];
const _hoisted_3 = ["id", "disabled", "aria-label", "aria-labelledby"];
const _hoisted_4 = { class: "b-form-file-text" };
const _hoisted_5 = { key: 0 };
const _hoisted_6 = {
  key: 1,
  class: "text-muted"
};
const _hoisted_7 = {
  key: 0,
  class: "b-form-file-drag-overlay"
};
const _hoisted_8 = { class: "b-form-file-drag-text" };
const _hoisted_9 = ["name", "form", "multiple", "disabled", "required", "accept", "capture", "directory", "webkitdirectory"];
const _hoisted_10 = ["id", "form", "name", "multiple", "disabled", "capture", "accept", "required", "aria-label", "aria-labelledby", "aria-required", "directory", "webkitdirectory"];
const _hoisted_11 = {
  key: 3,
  class: "b-form-file-display mt-2"
};
const _hoisted_12 = {
  key: 0,
  class: "small text-muted"
};
const _hoisted_13 = {
  key: 1,
  class: "small text-muted"
};
const _hoisted_14 = {
  key: 4,
  class: "visually-hidden",
  "aria-live": "polite",
  "aria-atomic": "true"
};
const _sfc_main = /* @__PURE__ */ defineComponent({
  ...{
    inheritAttrs: false
  },
  __name: "BFormFile",
  props: /* @__PURE__ */ mergeModels({
    ariaLabel: { default: void 0 },
    ariaLabelledby: { default: void 0 },
    accept: { default: "" },
    autofocus: { type: Boolean, default: false },
    browseText: { default: void 0 },
    capture: { default: void 0 },
    directory: { type: Boolean, default: false },
    disabled: { type: Boolean, default: false },
    dropPlaceholder: { default: void 0 },
    fileNameFormatter: { type: Function, default: void 0 },
    form: { default: void 0 },
    id: { default: void 0 },
    label: { default: "" },
    labelClass: { default: void 0 },
    multiple: { type: Boolean, default: false },
    name: { default: void 0 },
    noButton: { type: Boolean, default: false },
    noDrop: { type: Boolean, default: false },
    plain: { type: Boolean, default: false },
    placeholder: { default: "No file chosen" },
    required: { type: Boolean, default: false },
    showFileNames: { type: Boolean, default: false },
    size: { default: void 0 },
    state: { type: [Boolean, null], default: null }
  }, {
    "modelValue": {
      default: null
    },
    "modelModifiers": {}
  }),
  emits: /* @__PURE__ */ mergeModels(["change"], ["update:modelValue"]),
  setup(__props, { expose: __expose, emit: __emit }) {
    const _props = __props;
    const props = useDefaults(_props, "BFormFile");
    const slots = useSlots();
    const emit = __emit;
    const modelValue = useModel(__props, "modelValue");
    const attrs = useAttrs();
    const processedAttrs = computed(() => {
      if (props.plain) {
        return {
          rootAttrs: {},
          dropZoneAttrs: {},
          inputAttrs: attrs
        };
      }
      const { class: rootClass, style: rootStyle, title: dropZoneTitle, ...inputAttrs } = attrs;
      const rootAttrs = {};
      const dropZoneAttrs = {};
      if (rootClass !== void 0) rootAttrs.class = rootClass;
      if (rootStyle !== void 0) rootAttrs.style = rootStyle;
      if (dropZoneTitle !== void 0) dropZoneAttrs.title = dropZoneTitle;
      return {
        rootAttrs,
        dropZoneAttrs,
        inputAttrs
      };
    });
    const computedId = useId(() => props.id);
    const stateClass = useStateClass(() => props.state);
    const rootRef = useTemplateRef("rootRef");
    const dropZoneRef = useTemplateRef("dropZoneRef");
    const browseButtonRef = useTemplateRef("browseButtonRef");
    const plainInputRef = useTemplateRef("plainInputRef");
    const customInputRef = useTemplateRef("customInputRef");
    const computedAccept = computed(
      () => typeof props.accept === "string" ? props.accept : props.accept.join(",")
    );
    const {
      open,
      reset: resetDialog,
      onChange: onDialogChange
    } = useFileDialog({
      accept: computedAccept.value,
      multiple: props.multiple || props.directory,
      directory: props.directory,
      input: customInputRef
    });
    const { isOverDropZone } = useDropZone(dropZoneRef, {
      onDrop: (files) => {
        if (files && !props.noDrop) {
          handleFiles(files);
        }
      },
      multiple: props.multiple || props.directory
    });
    const hasLabelSlot = computed(() => !isEmptySlot(slots.label));
    const hasPlaceholderSlot = computed(() => !isEmptySlot(slots.placeholder));
    const computedClasses = computed(() => [
      stateClass.value,
      {
        [`form-control-${props.size}`]: props.size !== void 0
      }
    ]);
    const computedPlainClasses = computed(() => [
      "form-control",
      stateClass.value,
      {
        [`form-control-${props.size}`]: props.size !== void 0
      }
    ]);
    const internalFiles = ref([]);
    const selectedFiles = computed(() => internalFiles.value);
    const hasFiles = computed(() => selectedFiles.value.length > 0);
    const fileNames = computed(() => selectedFiles.value.map((file) => file.name));
    const formattedFileNames = computed(() => {
      if (!hasFiles.value) return "";
      if (props.fileNameFormatter) {
        return props.fileNameFormatter(selectedFiles.value);
      }
      const names = fileNames.value;
      if (names.length === 1) return names[0];
      return `${names.length} files selected`;
    });
    const showExternalDisplay = computed(
      () => !props.plain && props.showFileNames && (hasFiles.value || props.placeholder)
    );
    const ariaLiveMessage = computed(() => {
      if (!hasFiles.value) return "";
      const count = selectedFiles.value.length;
      if (count === 1) {
        return `File selected: ${selectedFiles.value[0].name}`;
      }
      return `${count} files selected`;
    });
    const effectiveBrowseText = computed(() => props.browseText ?? "Browse");
    const effectiveDropPlaceholder = computed(() => props.dropPlaceholder ?? "Drop files here...");
    const isFileAccepted = (file) => {
      if (!computedAccept.value) return true;
      const acceptTypes = computedAccept.value.split(",").map((type) => type.trim());
      return acceptTypes.some((acceptType) => {
        if (acceptType.startsWith(".")) {
          return file.name.toLowerCase().endsWith(acceptType.toLowerCase());
        }
        if (!acceptType.includes("*")) {
          return file.type === acceptType;
        }
        const slashIndex = acceptType.indexOf("/");
        if (slashIndex === -1) {
          return false;
        }
        const category = acceptType.slice(0, slashIndex);
        if (category === "*") {
          return true;
        }
        return file.type.startsWith(`${category}/`);
      });
    };
    const handleFiles = (files, nativeEvent) => {
      let fileArray = [];
      if (nativeEvent) {
        const input = nativeEvent.target;
        fileArray = input.files ? Array.from(input.files) : [];
      } else {
        fileArray = Array.from(files).filter((file) => isFileAccepted(file));
        if (customInputRef.value && typeof DataTransfer !== "undefined") {
          try {
            const dataTransfer = new DataTransfer();
            fileArray.forEach((file) => dataTransfer.items.add(file));
            customInputRef.value.files = dataTransfer.files;
          } catch {
          }
        }
      }
      internalFiles.value = fileArray;
      if (fileArray.length === 0) {
        modelValue.value = null;
      } else if (props.directory || props.multiple) {
        modelValue.value = fileArray;
      } else {
        const [firstFile] = fileArray;
        modelValue.value = firstFile;
      }
      nextTick(() => {
        if (nativeEvent) {
          emit("change", nativeEvent);
        } else {
          const changeEvent = new CustomEvent("change", {
            bubbles: true,
            cancelable: false,
            detail: {
              files: fileArray,
              target: { files: fileArray }
            }
          });
          Object.defineProperty(changeEvent, "files", {
            value: fileArray,
            enumerable: true
          });
          emit("change", changeEvent);
        }
      });
    };
    const openFileDialog = () => {
      if (!props.disabled) {
        open({
          accept: computedAccept.value,
          multiple: props.multiple || props.directory,
          directory: props.directory
        });
      }
    };
    const handleControlClick = () => {
      if (!props.disabled) {
        openFileDialog();
      }
    };
    const onPlainChange = (e) => {
      const input = e.target;
      if (input.files) {
        handleFiles(input.files, e);
      }
    };
    onDialogChange((files) => {
      if (files) {
        handleFiles(files);
      }
    });
    const reset = () => {
      internalFiles.value = [];
      modelValue.value = null;
      resetDialog();
      if (plainInputRef.value) {
        plainInputRef.value.value = "";
      }
    };
    const focus = () => {
      if (props.plain) {
        plainInputRef.value?.focus();
      } else {
        browseButtonRef.value?.focus();
      }
    };
    const blur = () => {
      if (props.plain) {
        plainInputRef.value?.blur();
      } else {
        browseButtonRef.value?.blur();
      }
    };
    onMounted(() => {
      if (props.autofocus) {
        nextTick(() => {
          focus();
        });
      }
    });
    watch(
      () => props.autofocus,
      (autofocus) => {
        if (autofocus) {
          focus();
        }
      }
    );
    watch(modelValue, (newValue) => {
      if (newValue === null) {
        internalFiles.value = [];
        if (plainInputRef.value) {
          plainInputRef.value.value = "";
        }
      } else if (Array.isArray(newValue)) {
        internalFiles.value = newValue;
      } else {
        internalFiles.value = [newValue];
      }
    });
    __expose({
      blur,
      element: computed(() => props.plain ? plainInputRef.value : browseButtonRef.value),
      focus,
      reset
    });
    return (_ctx, _cache) => {
      return openBlock(), createElementBlock("div", mergeProps({
        ref_key: "rootRef",
        ref: rootRef
      }, processedAttrs.value.rootAttrs, { class: "b-form-file-root" }), [
        hasLabelSlot.value || unref(props).label ? (openBlock(), createElementBlock("label", {
          key: 0,
          class: normalizeClass(["form-label", unref(props).labelClass]),
          for: unref(computedId)
        }, [
          renderSlot(_ctx.$slots, "label", {}, () => [
            createTextVNode(toDisplayString(unref(props).label), 1)
          ])
        ], 10, _hoisted_1)) : createCommentVNode("", true),
        !unref(props).plain ? (openBlock(), createElementBlock("div", mergeProps({
          key: 1,
          ref_key: "dropZoneRef",
          ref: dropZoneRef
        }, processedAttrs.value.dropZoneAttrs, {
          class: ["b-form-file-wrapper", {
            "b-form-file-dragging": unref(isOverDropZone) && !unref(props).noDrop,
            "b-form-file-has-files": hasFiles.value
          }]
        }), [
          createElementVNode("div", {
            class: normalizeClass(["b-form-file-control", computedClasses.value]),
            "aria-disabled": unref(props).disabled,
            onClick: handleControlClick
          }, [
            !unref(props).noButton ? (openBlock(), createElementBlock("button", {
              key: 0,
              id: unref(computedId),
              ref_key: "browseButtonRef",
              ref: browseButtonRef,
              type: "button",
              class: "b-form-file-button",
              disabled: unref(props).disabled,
              "aria-label": unref(props).ariaLabel,
              "aria-labelledby": unref(props).ariaLabelledby,
              onClick: withModifiers(openFileDialog, ["stop"])
            }, toDisplayString(effectiveBrowseText.value), 9, _hoisted_3)) : createCommentVNode("", true),
            createElementVNode("div", _hoisted_4, [
              renderSlot(_ctx.$slots, "file-name", {
                files: selectedFiles.value,
                names: fileNames.value
              }, () => [
                hasFiles.value ? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(formattedFileNames.value), 1)) : hasPlaceholderSlot.value || unref(props).placeholder ? (openBlock(), createElementBlock("span", _hoisted_6, [
                  renderSlot(_ctx.$slots, "placeholder", {}, () => [
                    createTextVNode(toDisplayString(unref(props).placeholder), 1)
                  ])
                ])) : createCommentVNode("", true)
              ])
            ])
          ], 10, _hoisted_2),
          unref(isOverDropZone) && !unref(props).noDrop ? (openBlock(), createElementBlock("div", _hoisted_7, [
            renderSlot(_ctx.$slots, "drop-placeholder", {}, () => [
              createElementVNode("div", _hoisted_8, toDisplayString(effectiveDropPlaceholder.value), 1)
            ])
          ])) : createCommentVNode("", true),
          createElementVNode("input", mergeProps({
            ref_key: "customInputRef",
            ref: customInputRef
          }, processedAttrs.value.inputAttrs, {
            type: "file",
            name: unref(props).name,
            form: unref(props).form,
            multiple: unref(props).multiple || unref(props).directory,
            disabled: unref(props).disabled,
            required: unref(props).required,
            accept: computedAccept.value || void 0,
            capture: unref(props).capture,
            directory: unref(props).directory || void 0,
            webkitdirectory: unref(props).directory || void 0,
            tabindex: "-1",
            "aria-hidden": "true",
            style: { "position": "absolute", "z-index": "-5", "width": "0", "height": "0", "opacity": "0", "overflow": "hidden", "pointer-events": "none" }
          }), null, 16, _hoisted_9)
        ], 16)) : (openBlock(), createElementBlock("input", mergeProps({
          key: 2,
          id: unref(computedId),
          ref_key: "plainInputRef",
          ref: plainInputRef
        }, processedAttrs.value.inputAttrs, {
          type: "file",
          class: computedPlainClasses.value,
          form: unref(props).form,
          name: unref(props).name,
          multiple: unref(props).multiple || unref(props).directory,
          disabled: unref(props).disabled,
          capture: unref(props).capture,
          accept: computedAccept.value || void 0,
          required: unref(props).required || void 0,
          "aria-label": unref(props).ariaLabel,
          "aria-labelledby": unref(props).ariaLabelledby,
          "aria-required": unref(props).required || void 0,
          directory: unref(props).directory || void 0,
          webkitdirectory: unref(props).directory || void 0,
          onChange: onPlainChange
        }), null, 16, _hoisted_10)),
        showExternalDisplay.value ? (openBlock(), createElementBlock("div", _hoisted_11, [
          renderSlot(_ctx.$slots, "file-name", {
            files: selectedFiles.value,
            names: fileNames.value
          }, () => [
            hasFiles.value ? (openBlock(), createElementBlock("div", _hoisted_12, toDisplayString(formattedFileNames.value), 1)) : hasPlaceholderSlot.value || unref(props).placeholder ? (openBlock(), createElementBlock("div", _hoisted_13, [
              renderSlot(_ctx.$slots, "placeholder", {}, () => [
                createTextVNode(toDisplayString(unref(props).placeholder), 1)
              ])
            ])) : createCommentVNode("", true)
          ])
        ])) : createCommentVNode("", true),
        !unref(props).plain ? (openBlock(), createElementBlock("div", _hoisted_14, toDisplayString(ariaLiveMessage.value), 1)) : createCommentVNode("", true)
      ], 16);
    };
  }
});
export {
  _sfc_main as _
};
//# sourceMappingURL=BFormFile.vue_vue_type_script_setup_true_lang-BvjnbxU1.mjs.map
