Files
2026-01-09 19:24:41 +00:00

427 lines
13 KiB
JavaScript

import {
VCounter,
VField,
makeVFieldProps
} from "./chunk-ITO3JWXJ.js";
import {
VChip
} from "./chunk-PLNIG6WP.js";
import {
VInput,
makeVInputProps,
useFocus
} from "./chunk-IK67YVHF.js";
import {
forwardRefs
} from "./chunk-GUNW7S2L.js";
import {
useLocale
} from "./chunk-ENTLCOCZ.js";
import {
useProxiedModel
} from "./chunk-KA77II4V.js";
import {
callEvent,
filterInputAttrs,
genericComponent,
humanReadableFileSize,
propsFactory,
useRender,
wrapInArray
} from "./chunk-LZ5T2ERG.js";
import {
Fragment,
computed,
createBaseVNode,
createVNode,
mergeProps,
nextTick,
normalizeClass,
ref,
shallowRef,
toRef,
watch
} from "./chunk-2MKFL3BX.js";
// node_modules/vuetify/lib/components/VFileInput/VFileInput.js
import "/home/david/gitea_repo/personal_development/ServerManagerWebApp/frontend/node_modules/vuetify/lib/components/VFileInput/VFileInput.css";
// node_modules/vuetify/lib/composables/fileDrop.js
function useFileDrop() {
function hasFilesOrFolders(e) {
const entries = [...e.dataTransfer?.items ?? []].filter((x) => x.kind === "file").map((x) => x.webkitGetAsEntry()).filter(Boolean);
return entries.length > 0 || [...e.dataTransfer?.files ?? []].length > 0;
}
async function handleDrop(e) {
const result = [];
const entries = [...e.dataTransfer?.items ?? []].filter((x) => x.kind === "file").map((x) => x.webkitGetAsEntry()).filter(Boolean);
if (entries.length) {
for (const entry of entries) {
const files = await traverseFileTree(entry, appendIfDirectory(".", entry));
result.push(...files.map((x) => x.file));
}
} else {
result.push(...[...e.dataTransfer?.files ?? []]);
}
return result;
}
return {
handleDrop,
hasFilesOrFolders
};
}
function traverseFileTree(item) {
let path = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "";
return new Promise((resolve, reject) => {
if (item.isFile) {
const fileEntry = item;
fileEntry.file((file) => resolve([{
file,
path
}]), reject);
} else if (item.isDirectory) {
const directoryReader = item.createReader();
directoryReader.readEntries(async (entries) => {
const files = [];
for (const entry of entries) {
files.push(...await traverseFileTree(entry, appendIfDirectory(path, entry)));
}
resolve(files);
});
}
});
}
function appendIfDirectory(path, item) {
return item.isDirectory ? `${path}/${item.name}` : path;
}
// node_modules/vuetify/lib/composables/fileFilter.js
var makeFileFilterProps = propsFactory({
filterByType: String
}, "file-accept");
function useFileFilter(props) {
const fileFilter = computed(() => props.filterByType ? createFilter(props.filterByType) : null);
function filterAccepted(files) {
if (fileFilter.value) {
const accepted = files.filter(fileFilter.value);
return {
accepted,
rejected: files.filter((f) => !accepted.includes(f))
};
}
return {
accepted: files,
rejected: []
};
}
return {
filterAccepted
};
}
function createFilter(v) {
const types = v.split(",").map((x) => x.trim().toLowerCase());
const extensionsToMatch = types.filter((x) => x.startsWith("."));
const wildcards = types.filter((x) => x.endsWith("/*"));
const typesToMatch = types.filter((x) => !extensionsToMatch.includes(x) && !wildcards.includes(x));
return (file) => {
const extension = file.name.split(".").at(-1)?.toLowerCase() ?? "";
const typeGroup = file.type.split("/").at(0)?.toLowerCase() ?? "";
return typesToMatch.includes(file.type) || extensionsToMatch.includes(`.${extension}`) || wildcards.includes(`${typeGroup}/*`);
};
}
// node_modules/vuetify/lib/components/VFileInput/VFileInput.js
var makeVFileInputProps = propsFactory({
chips: Boolean,
counter: Boolean,
counterSizeString: {
type: String,
default: "$vuetify.fileInput.counterSize"
},
counterString: {
type: String,
default: "$vuetify.fileInput.counter"
},
hideInput: Boolean,
multiple: Boolean,
showSize: {
type: [Boolean, Number, String],
default: false,
validator: (v) => {
return typeof v === "boolean" || [1e3, 1024].includes(Number(v));
}
},
truncateLength: {
type: [Number, String],
default: 22
},
...makeVInputProps({
prependIcon: "$file"
}),
modelValue: {
type: [Array, Object],
default: (props) => props.multiple ? [] : null,
validator: (val) => {
return wrapInArray(val).every((v) => v != null && typeof v === "object");
}
},
...makeFileFilterProps(),
...makeVFieldProps({
clearable: true
})
}, "VFileInput");
var VFileInput = genericComponent()({
name: "VFileInput",
inheritAttrs: false,
props: makeVFileInputProps(),
emits: {
"click:control": (e) => true,
"mousedown:control": (e) => true,
"update:focused": (focused) => true,
"update:modelValue": (files) => true,
rejected: (files) => true
},
setup(props, _ref) {
let {
attrs,
emit,
slots
} = _ref;
const {
t
} = useLocale();
const {
filterAccepted
} = useFileFilter(props);
const model = useProxiedModel(props, "modelValue", props.modelValue, (val) => wrapInArray(val), (val) => !props.multiple && Array.isArray(val) ? val[0] : val);
const {
isFocused,
focus,
blur
} = useFocus(props);
const base = computed(() => typeof props.showSize !== "boolean" ? props.showSize : void 0);
const totalBytes = computed(() => (model.value ?? []).reduce((bytes, _ref2) => {
let {
size = 0
} = _ref2;
return bytes + size;
}, 0));
const totalBytesReadable = computed(() => humanReadableFileSize(totalBytes.value, base.value));
const fileNames = computed(() => (model.value ?? []).map((file) => {
const {
name = "",
size = 0
} = file;
const truncatedText = truncateText(name);
return !props.showSize ? truncatedText : `${truncatedText} (${humanReadableFileSize(size, base.value)})`;
}));
const counterValue = computed(() => {
const fileCount = model.value?.length ?? 0;
if (props.showSize) return t(props.counterSizeString, fileCount, totalBytesReadable.value);
else return t(props.counterString, fileCount);
});
const vInputRef = ref();
const vFieldRef = ref();
const inputRef = ref();
const isActive = toRef(() => isFocused.value || props.active);
const isPlainOrUnderlined = computed(() => ["plain", "underlined"].includes(props.variant));
const isDragging = shallowRef(false);
const {
handleDrop,
hasFilesOrFolders
} = useFileDrop();
function onFocus() {
if (inputRef.value !== document.activeElement) {
inputRef.value?.focus();
}
if (!isFocused.value) focus();
}
function onClickPrepend(e) {
inputRef.value?.click();
}
function onControlMousedown(e) {
emit("mousedown:control", e);
}
function onControlClick(e) {
inputRef.value?.click();
emit("click:control", e);
}
function onClear(e) {
e.stopPropagation();
onFocus();
nextTick(() => {
model.value = [];
callEvent(props["onClick:clear"], e);
});
}
function truncateText(str) {
if (str.length < Number(props.truncateLength)) return str;
const charsKeepOneSide = Math.floor((Number(props.truncateLength) - 1) / 2);
return `${str.slice(0, charsKeepOneSide)}…${str.slice(str.length - charsKeepOneSide)}`;
}
function onDragover(e) {
e.preventDefault();
e.stopImmediatePropagation();
isDragging.value = true;
}
function onDragleave(e) {
e.preventDefault();
isDragging.value = false;
}
async function onDrop(e) {
e.preventDefault();
e.stopImmediatePropagation();
isDragging.value = false;
if (!inputRef.value || !hasFilesOrFolders(e)) return;
const allDroppedFiles = await handleDrop(e);
selectAccepted(allDroppedFiles);
}
function onFileSelection(e) {
if (!e.target || e.repack) return;
if (!props.filterByType) {
const target = e.target;
model.value = [...target.files ?? []];
} else {
selectAccepted([...e.target.files]);
}
}
function selectAccepted(files) {
const dataTransfer = new DataTransfer();
const {
accepted,
rejected
} = filterAccepted(files);
if (rejected.length) {
emit("rejected", rejected);
}
for (const file of accepted) {
dataTransfer.items.add(file);
}
inputRef.value.files = dataTransfer.files;
model.value = [...dataTransfer.files];
const event = new Event("change", {
bubbles: true
});
event.repack = true;
inputRef.value.dispatchEvent(event);
}
watch(model, (newValue) => {
const hasModelReset = !Array.isArray(newValue) || !newValue.length;
if (hasModelReset && inputRef.value) {
inputRef.value.value = "";
}
});
useRender(() => {
const hasCounter = !!(slots.counter || props.counter);
const hasDetails = !!(hasCounter || slots.details);
const [rootAttrs, inputAttrs] = filterInputAttrs(attrs);
const {
modelValue: _,
...inputProps
} = VInput.filterProps(props);
const fieldProps = {
...VField.filterProps(props),
"onClick:clear": onClear
};
const expectsDirectory = attrs.webkitdirectory !== void 0 && attrs.webkitdirectory !== false;
const acceptFallback = attrs.accept ? String(attrs.accept) : void 0;
const inputAccept = expectsDirectory ? void 0 : props.filterByType ?? acceptFallback;
return createVNode(VInput, mergeProps({
"ref": vInputRef,
"modelValue": props.multiple ? model.value : model.value[0],
"class": ["v-file-input", {
"v-file-input--chips": !!props.chips,
"v-file-input--dragging": isDragging.value,
"v-file-input--hide": props.hideInput,
"v-input--plain-underlined": isPlainOrUnderlined.value
}, props.class],
"style": props.style,
"onClick:prepend": onClickPrepend
}, rootAttrs, inputProps, {
"centerAffix": !isPlainOrUnderlined.value,
"focused": isFocused.value
}), {
...slots,
default: (_ref3) => {
let {
id,
isDisabled,
isDirty,
isReadonly,
isValid,
hasDetails: hasDetails2
} = _ref3;
return createVNode(VField, mergeProps({
"ref": vFieldRef,
"prependIcon": props.prependIcon,
"onMousedown": onControlMousedown,
"onClick": onControlClick,
"onClick:prependInner": props["onClick:prependInner"],
"onClick:appendInner": props["onClick:appendInner"]
}, fieldProps, {
"id": id.value,
"active": isActive.value || isDirty.value,
"dirty": isDirty.value || props.dirty,
"disabled": isDisabled.value,
"focused": isFocused.value,
"details": hasDetails2.value,
"error": isValid.value === false,
"onDragover": onDragover,
"onDrop": onDrop
}), {
...slots,
default: (_ref4) => {
let {
props: {
class: fieldClass,
...slotProps
},
controlRef
} = _ref4;
return createBaseVNode(Fragment, null, [createBaseVNode("input", mergeProps({
"ref": (val) => inputRef.value = controlRef.value = val,
"type": "file",
"accept": inputAccept,
"readonly": isReadonly.value,
"disabled": isDisabled.value,
"multiple": props.multiple,
"name": props.name,
"onClick": (e) => {
e.stopPropagation();
if (isReadonly.value) e.preventDefault();
onFocus();
},
"onChange": onFileSelection,
"onDragleave": onDragleave,
"onFocus": onFocus,
"onBlur": blur
}, slotProps, inputAttrs), null), createBaseVNode("div", {
"class": normalizeClass(fieldClass)
}, [!!model.value?.length && !props.hideInput && (slots.selection ? slots.selection({
fileNames: fileNames.value,
totalBytes: totalBytes.value,
totalBytesReadable: totalBytesReadable.value
}) : props.chips ? fileNames.value.map((text) => createVNode(VChip, {
"key": text,
"size": "small",
"text": text
}, null)) : fileNames.value.join(", "))])]);
}
});
},
details: hasDetails ? (slotProps) => createBaseVNode(Fragment, null, [slots.details?.(slotProps), hasCounter && createBaseVNode(Fragment, null, [createBaseVNode("span", null, null), createVNode(VCounter, {
"active": !!model.value?.length,
"value": counterValue.value,
"disabled": props.disabled
}, slots.counter)])]) : void 0
});
});
return forwardRefs({}, vInputRef, vFieldRef, inputRef);
}
});
export {
VFileInput
};
//# sourceMappingURL=chunk-EKMPZMZF.js.map