bootstrap-vue-next
Version:
Seamless integration of Vue 3, Bootstrap 5, and TypeScript for modern, type-safe UI development
418 lines (417 loc) • 15.8 kB
JavaScript
require("./chunk-CoQrYLCe.js");
const require_dist = require("./dist-BJ15ThEs.js");
const require_dom = require("./dom-Bs6DzM72.js");
const require_useDefaults = require("./useDefaults-DsLf4iRY.js");
const require_useId = require("./useId-DHrBgM7P.js");
const require_useStateClass = require("./useStateClass-CJ24hpkn.js");
let vue = require("vue");
//#region src/components/BFormFile/BFormFile.vue?vue&type=script&setup=true&lang.ts
var _hoisted_1 = ["for"];
var _hoisted_2 = ["aria-disabled"];
var _hoisted_3 = [
"id",
"disabled",
"aria-label",
"aria-labelledby"
];
var _hoisted_4 = { class: "b-form-file-text" };
var _hoisted_5 = { key: 0 };
var _hoisted_6 = {
key: 1,
class: "text-muted"
};
var _hoisted_7 = {
key: 0,
class: "b-form-file-drag-overlay"
};
var _hoisted_8 = { class: "b-form-file-drag-text" };
var _hoisted_9 = [
"name",
"form",
"multiple",
"disabled",
"required",
"accept",
"capture",
"directory",
"webkitdirectory"
];
var _hoisted_10 = [
"id",
"form",
"name",
"multiple",
"disabled",
"capture",
"accept",
"required",
"aria-label",
"aria-labelledby",
"aria-required",
"directory",
"webkitdirectory"
];
var _hoisted_11 = {
key: 3,
class: "b-form-file-display mt-2"
};
var _hoisted_12 = {
key: 0,
class: "small text-muted"
};
var _hoisted_13 = {
key: 1,
class: "small text-muted"
};
var _hoisted_14 = {
key: 4,
class: "visually-hidden",
"aria-live": "polite",
"aria-atomic": "true"
};
//#endregion
//#region src/components/BFormFile/BFormFile.vue
var BFormFile_default = /* @__PURE__ */ (0, vue.defineComponent)({
inheritAttrs: false,
__name: "BFormFile",
props: /* @__PURE__ */ (0, vue.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__ */ (0, vue.mergeModels)(["change"], ["update:modelValue"]),
setup(__props, { expose: __expose, emit: __emit }) {
const props = require_useDefaults.useDefaults(__props, "BFormFile");
const slots = (0, vue.useSlots)();
const emit = __emit;
const modelValue = (0, vue.useModel)(__props, "modelValue");
const attrs = (0, vue.useAttrs)();
const processedAttrs = (0, vue.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 = require_useId.useId(() => props.id);
const stateClass = require_useStateClass.useStateClass(() => props.state);
const rootRef = (0, vue.useTemplateRef)("rootRef");
const dropZoneRef = (0, vue.useTemplateRef)("dropZoneRef");
const browseButtonRef = (0, vue.useTemplateRef)("browseButtonRef");
const plainInputRef = (0, vue.useTemplateRef)("plainInputRef");
const customInputRef = (0, vue.useTemplateRef)("customInputRef");
const computedAccept = (0, vue.computed)(() => typeof props.accept === "string" ? props.accept : props.accept.join(","));
const { open, reset: resetDialog, onChange: onDialogChange } = require_dist.useFileDialog({
accept: computedAccept.value,
multiple: props.multiple || props.directory,
directory: props.directory,
input: customInputRef
});
const { isOverDropZone } = require_dist.useDropZone(dropZoneRef, {
onDrop: (files) => {
if (files && !props.noDrop) handleFiles(files);
},
multiple: props.multiple || props.directory
});
const hasLabelSlot = (0, vue.computed)(() => !require_dom.isEmptySlot(slots.label));
const hasPlaceholderSlot = (0, vue.computed)(() => !require_dom.isEmptySlot(slots.placeholder));
const computedClasses = (0, vue.computed)(() => [stateClass.value, { [`form-control-${props.size}`]: props.size !== void 0 }]);
const computedPlainClasses = (0, vue.computed)(() => [
"form-control",
stateClass.value,
{ [`form-control-${props.size}`]: props.size !== void 0 }
]);
const internalFiles = (0, vue.ref)([]);
const selectedFiles = (0, vue.computed)(() => internalFiles.value);
const hasFiles = (0, vue.computed)(() => selectedFiles.value.length > 0);
const fileNames = (0, vue.computed)(() => selectedFiles.value.map((file) => file.name));
const formattedFileNames = (0, vue.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 = (0, vue.computed)(() => !props.plain && props.showFileNames && (hasFiles.value || props.placeholder));
const ariaLiveMessage = (0, vue.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 = (0, vue.computed)(() => props.browseText ?? "Browse");
const effectiveDropPlaceholder = (0, vue.computed)(() => props.dropPlaceholder ?? "Drop files here...");
const isFileAccepted = (file) => {
if (!computedAccept.value) return true;
return computedAccept.value.split(",").map((type) => type.trim()).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;
if (firstFile) modelValue.value = firstFile;
}
(0, vue.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();
};
(0, vue.onMounted)(() => {
if (props.autofocus) (0, vue.nextTick)(() => {
focus();
});
});
(0, vue.watch)(() => props.autofocus, (autofocus) => {
if (autofocus) focus();
});
(0, vue.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: (0, vue.computed)(() => props.plain ? plainInputRef.value : browseButtonRef.value),
focus,
reset
});
return (_ctx, _cache) => {
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", (0, vue.mergeProps)({
ref_key: "rootRef",
ref: rootRef
}, processedAttrs.value.rootAttrs, { class: "b-form-file-root" }), [
hasLabelSlot.value || (0, vue.unref)(props).label ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("label", {
key: 0,
class: (0, vue.normalizeClass)(["form-label", (0, vue.unref)(props).labelClass]),
for: (0, vue.unref)(computedId)
}, [(0, vue.renderSlot)(_ctx.$slots, "label", {}, () => [(0, vue.createTextVNode)((0, vue.toDisplayString)((0, vue.unref)(props).label), 1)])], 10, _hoisted_1)) : (0, vue.createCommentVNode)("", true),
!(0, vue.unref)(props).plain ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", (0, vue.mergeProps)({
key: 1,
ref_key: "dropZoneRef",
ref: dropZoneRef
}, processedAttrs.value.dropZoneAttrs, { class: ["b-form-file-wrapper", {
"b-form-file-dragging": (0, vue.unref)(isOverDropZone) && !(0, vue.unref)(props).noDrop,
"b-form-file-has-files": hasFiles.value
}] }), [
(0, vue.createElementVNode)("div", {
class: (0, vue.normalizeClass)(["b-form-file-control", computedClasses.value]),
"aria-disabled": (0, vue.unref)(props).disabled,
onClick: handleControlClick
}, [!(0, vue.unref)(props).noButton ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
key: 0,
id: (0, vue.unref)(computedId),
ref_key: "browseButtonRef",
ref: browseButtonRef,
type: "button",
class: "b-form-file-button",
disabled: (0, vue.unref)(props).disabled,
"aria-label": (0, vue.unref)(props).ariaLabel,
"aria-labelledby": (0, vue.unref)(props).ariaLabelledby,
onClick: (0, vue.withModifiers)(openFileDialog, ["stop"])
}, (0, vue.toDisplayString)(effectiveBrowseText.value), 9, _hoisted_3)) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div", _hoisted_4, [(0, vue.renderSlot)(_ctx.$slots, "file-name", {
files: selectedFiles.value,
names: fileNames.value
}, () => [hasFiles.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_5, (0, vue.toDisplayString)(formattedFileNames.value), 1)) : hasPlaceholderSlot.value || (0, vue.unref)(props).placeholder ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_6, [(0, vue.renderSlot)(_ctx.$slots, "placeholder", {}, () => [(0, vue.createTextVNode)((0, vue.toDisplayString)((0, vue.unref)(props).placeholder), 1)])])) : (0, vue.createCommentVNode)("", true)])])], 10, _hoisted_2),
(0, vue.unref)(isOverDropZone) && !(0, vue.unref)(props).noDrop ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_7, [(0, vue.renderSlot)(_ctx.$slots, "drop-placeholder", {}, () => [(0, vue.createElementVNode)("div", _hoisted_8, (0, vue.toDisplayString)(effectiveDropPlaceholder.value), 1)])])) : (0, vue.createCommentVNode)("", true),
(0, vue.createElementVNode)("input", (0, vue.mergeProps)({
ref_key: "customInputRef",
ref: customInputRef
}, processedAttrs.value.inputAttrs, {
type: "file",
name: (0, vue.unref)(props).name,
form: (0, vue.unref)(props).form,
multiple: (0, vue.unref)(props).multiple || (0, vue.unref)(props).directory,
disabled: (0, vue.unref)(props).disabled,
required: (0, vue.unref)(props).required,
accept: computedAccept.value || void 0,
capture: (0, vue.unref)(props).capture,
directory: (0, vue.unref)(props).directory || void 0,
webkitdirectory: (0, vue.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)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("input", (0, vue.mergeProps)({
key: 2,
id: (0, vue.unref)(computedId),
ref_key: "plainInputRef",
ref: plainInputRef
}, processedAttrs.value.inputAttrs, {
type: "file",
class: computedPlainClasses.value,
form: (0, vue.unref)(props).form,
name: (0, vue.unref)(props).name,
multiple: (0, vue.unref)(props).multiple || (0, vue.unref)(props).directory,
disabled: (0, vue.unref)(props).disabled,
capture: (0, vue.unref)(props).capture,
accept: computedAccept.value || void 0,
required: (0, vue.unref)(props).required || void 0,
"aria-label": (0, vue.unref)(props).ariaLabel,
"aria-labelledby": (0, vue.unref)(props).ariaLabelledby,
"aria-required": (0, vue.unref)(props).required || void 0,
directory: (0, vue.unref)(props).directory || void 0,
webkitdirectory: (0, vue.unref)(props).directory || void 0,
onChange: onPlainChange
}), null, 16, _hoisted_10)),
showExternalDisplay.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_11, [(0, vue.renderSlot)(_ctx.$slots, "file-name", {
files: selectedFiles.value,
names: fileNames.value
}, () => [hasFiles.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_12, (0, vue.toDisplayString)(formattedFileNames.value), 1)) : hasPlaceholderSlot.value || (0, vue.unref)(props).placeholder ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_13, [(0, vue.renderSlot)(_ctx.$slots, "placeholder", {}, () => [(0, vue.createTextVNode)((0, vue.toDisplayString)((0, vue.unref)(props).placeholder), 1)])])) : (0, vue.createCommentVNode)("", true)])])) : (0, vue.createCommentVNode)("", true),
!(0, vue.unref)(props).plain ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_14, (0, vue.toDisplayString)(ariaLiveMessage.value), 1)) : (0, vue.createCommentVNode)("", true)
], 16);
};
}
});
//#endregion
Object.defineProperty(exports, "BFormFile_default", {
enumerable: true,
get: function() {
return BFormFile_default;
}
});
//# sourceMappingURL=BFormFile-BWknj7fW.js.map