@varlet/ui
Version:
A Vue3 component library based on Material Design 2 and 3, supporting mobile and desktop.
518 lines (517 loc) • 19.1 kB
JavaScript
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
import { computed, defineComponent, nextTick, reactive, ref, watch } from "vue";
import { call, isNumber, normalizeToArray, toDataURL, toNumber } from "@varlet/shared";
import { useEventListener } from "@varlet/use";
import VarFormDetails from "../form-details/index.mjs";
import { useForm } from "../form/provide.mjs";
import Hover from "../hover/index.mjs";
import VarHoverOverlay, { useHoverOverlay } from "../hover-overlay/index.mjs";
import VarIcon from "../icon/index.mjs";
import ImagePreview from "../image-preview/index.mjs";
import VarPopup from "../popup/index.mjs";
import Ripple from "../ripple/index.mjs";
import { createNamespace, formatElevation, useValidation } from "../utils/components.mjs";
import { toSizeUnit } from "../utils/elements.mjs";
import { isHTMLSupportImage, isHTMLSupportVideo } from "../utils/shared.mjs";
import { props } from "./props.mjs";
const { name, n, classes } = createNamespace("uploader");
let fid = 0;
import { renderList as _renderList, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock, toDisplayString as _toDisplayString, normalizeClass as _normalizeClass, createElementVNode as _createElementVNode, renderSlot as _renderSlot, resolveComponent as _resolveComponent, createVNode as _createVNode, withModifiers as _withModifiers, createCommentVNode as _createCommentVNode, normalizeStyle as _normalizeStyle, resolveDirective as _resolveDirective, withDirectives as _withDirectives, withCtx as _withCtx, createSlots as _createSlots } from "vue";
const _hoisted_1 = ["onClick"];
const _hoisted_2 = ["onClick"];
const _hoisted_3 = ["src", "alt"];
const _hoisted_4 = ["tabindex"];
const _hoisted_5 = ["multiple", "accept", "capture", "disabled"];
const _hoisted_6 = ["src"];
function __render__(_ctx, _cache) {
const _component_var_icon = _resolveComponent("var-icon");
const _component_var_hover_overlay = _resolveComponent("var-hover-overlay");
const _component_var_form_details = _resolveComponent("var-form-details");
const _component_var_popup = _resolveComponent("var-popup");
const _directive_ripple = _resolveDirective("ripple");
const _directive_hover = _resolveDirective("hover");
return _openBlock(), _createElementBlock(
"div",
{
class: _normalizeClass(_ctx.classes(_ctx.n(), _ctx.n("$--box")))
},
[
_createElementVNode(
"div",
{
class: _normalizeClass(_ctx.n("file-list"))
},
[
(_openBlock(true), _createElementBlock(
_Fragment,
null,
_renderList(_ctx.files, (f) => {
return _withDirectives((_openBlock(), _createElementBlock("div", {
key: f.id,
class: _normalizeClass(_ctx.classes(_ctx.n("file"), _ctx.formatElevation(_ctx.elevation, 2), [f.state === "loading", _ctx.n("--loading")])),
onClick: ($event) => _ctx.preview(f)
}, [
_createElementVNode(
"div",
{
class: _normalizeClass(_ctx.n("file-name"))
},
_toDisplayString(f.name || f.url),
3
/* TEXT, CLASS */
),
_ctx.removable ? _renderSlot(_ctx.$slots, "remove-button", {
key: 0,
remove: () => {
_ctx.handleRemove(f);
}
}, () => [
_createElementVNode("div", {
class: _normalizeClass(_ctx.n("file-close")),
onClick: _withModifiers(($event) => _ctx.handleRemove(f), ["stop"])
}, [
_createVNode(_component_var_icon, {
class: _normalizeClass(_ctx.n("file-close-icon")),
"var-uploader-cover": "",
name: "delete"
}, null, 8, ["class"])
], 10, _hoisted_2)
]) : _createCommentVNode("v-if", true),
f.cover ? (_openBlock(), _createElementBlock("img", {
key: 1,
role: "img",
class: _normalizeClass(_ctx.n("file-cover")),
style: _normalizeStyle({ objectFit: f.fit }),
src: f.cover,
alt: f.name
}, null, 14, _hoisted_3)) : _createCommentVNode("v-if", true),
_createElementVNode(
"div",
{
class: _normalizeClass(_ctx.n("file-indicator"))
},
[
_createElementVNode(
"div",
{
class: _normalizeClass(
_ctx.classes(_ctx.n("progress"), [f.state === "success", _ctx.n("--success")], [f.state === "error", _ctx.n("--error")])
),
style: _normalizeStyle({ width: f.state === "success" || f.state === "error" ? "100%" : `${f.progress}%` })
},
null,
6
/* CLASS, STYLE */
)
],
2
/* CLASS */
)
], 10, _hoisted_1)), [
[_directive_ripple, { disabled: _ctx.disabled || _ctx.formDisabled || _ctx.readonly || _ctx.formReadonly || !_ctx.ripple }]
]);
}),
128
/* KEYED_FRAGMENT */
)),
!_ctx.maxlength || _ctx.modelValue.length < _ctx.toNumber(_ctx.maxlength) ? _withDirectives((_openBlock(), _createElementBlock("div", {
key: 0,
ref: "actionRef",
class: _normalizeClass(
_ctx.classes(
_ctx.n("--outline-none"),
[!_ctx.$slots.default, `${_ctx.n("action")} ${_ctx.formatElevation(_ctx.elevation, 2)}`],
[_ctx.disabled || _ctx.formDisabled, _ctx.n("--disabled")]
)
),
tabindex: _ctx.disabled || _ctx.formDisabled ? void 0 : "0",
onClick: _cache[2] || (_cache[2] = (...args) => _ctx.handleActionClick && _ctx.handleActionClick(...args)),
onFocus: _cache[3] || (_cache[3] = ($event) => _ctx.isFocusing = true),
onBlur: _cache[4] || (_cache[4] = ($event) => _ctx.isFocusing = false)
}, [
_createElementVNode("input", {
ref: "input",
type: "file",
class: _normalizeClass(_ctx.n("action-input")),
multiple: _ctx.multiple,
accept: _ctx.accept,
capture: _ctx.capture,
disabled: _ctx.disabled || _ctx.formDisabled || _ctx.readonly || _ctx.formReadonly,
onChange: _cache[0] || (_cache[0] = (...args) => _ctx.handleChange && _ctx.handleChange(...args)),
onClick: _cache[1] || (_cache[1] = _withModifiers(() => {
}, ["stop"]))
}, null, 42, _hoisted_5),
_renderSlot(_ctx.$slots, "default", {}, () => [
_createVNode(_component_var_icon, {
class: _normalizeClass(_ctx.n("action-icon")),
"var-uploader-cover": "",
name: "plus"
}, null, 8, ["class"]),
_createVNode(_component_var_hover_overlay, {
hovering: _ctx.hovering && !_ctx.disabled && !_ctx.formDisabled && !_ctx.readonly && !_ctx.formReadonly,
focusing: _ctx.isFocusing && !_ctx.disabled && !_ctx.formDisabled && !_ctx.readonly && !_ctx.formReadonly
}, null, 8, ["hovering", "focusing"])
])
], 42, _hoisted_4)), [
[_directive_ripple, {
disabled: _ctx.disabled || _ctx.formDisabled || _ctx.readonly || _ctx.formReadonly || !_ctx.ripple || Boolean(_ctx.$slots.default)
}],
[_directive_hover, _ctx.handleHovering, "desktop"]
]) : _createCommentVNode("v-if", true)
],
2
/* CLASS */
),
_createVNode(_component_var_form_details, {
"error-message": _ctx.errorMessage,
"extra-message": _ctx.maxlengthText
}, _createSlots({
_: 2
/* DYNAMIC */
}, [
_ctx.$slots["extra-message"] ? {
name: "extra-message",
fn: _withCtx(() => [
_renderSlot(_ctx.$slots, "extra-message")
]),
key: "0"
} : void 0
]), 1032, ["error-message", "extra-message"]),
_createVNode(_component_var_popup, {
show: _ctx.showPreview,
"onUpdate:show": _cache[5] || (_cache[5] = ($event) => _ctx.showPreview = $event),
class: _normalizeClass(_ctx.n("preview")),
"var-uploader-cover": "",
position: "center",
onClosed: _cache[6] || (_cache[6] = ($event) => _ctx.currentPreview = null)
}, {
default: _withCtx(() => {
var _a, _b;
return [
_ctx.currentPreview && _ctx.isHTMLSupportVideo((_a = _ctx.currentPreview) == null ? void 0 : _a.url) ? (_openBlock(), _createElementBlock("video", {
key: 0,
class: _normalizeClass(_ctx.n("preview-video")),
playsinline: "true",
"webkit-playsinline": "true",
"x5-playsinline": "true",
"x5-video-player-type": "h5",
"x5-video-player-fullscreen": "false",
controls: "",
src: (_b = _ctx.currentPreview) == null ? void 0 : _b.url
}, null, 10, _hoisted_6)) : _createCommentVNode("v-if", true)
];
}),
_: 1
/* STABLE */
}, 8, ["show", "class"])
],
2
/* CLASS */
);
}
const __sfc__ = defineComponent({
name,
directives: { Ripple, Hover },
components: {
VarIcon,
VarPopup,
VarFormDetails,
VarHoverOverlay
},
props,
setup(props2) {
const isFocusing = ref(false);
const actionRef = ref(null);
const input = ref(null);
const showPreview = ref(false);
const currentPreview = ref(null);
const maxlengthText = computed(() => {
const {
maxlength,
modelValue: { length }
} = props2;
return isNumber(maxlength) ? `${length} / ${maxlength}` : "";
});
const { form, bindForm } = useForm();
const {
errorMessage,
validateWithTrigger: vt,
validate: v,
// expose
resetValidation
} = useValidation();
const { hovering, handleHovering } = useHoverOverlay();
const files = computed(() => {
const { modelValue, hideList } = props2;
if (hideList) {
return [];
}
return modelValue;
});
let callReset = false;
const varFileUtils = {
getSuccess,
getError,
getLoading
};
const uploaderProvider = {
validate,
resetValidation,
reset
};
call(bindForm, uploaderProvider);
useEventListener(() => window, "keydown", handleKeydown);
useEventListener(() => window, "keyup", handleKeyup);
watch(
() => props2.modelValue,
() => {
!callReset && validateWithTrigger("onChange");
callReset = false;
},
{ deep: true }
);
function handleKeydown(event) {
if (!isFocusing.value) {
return;
}
if (event.key === " " || event.key === "Enter") {
event.preventDefault();
}
if (event.key === "Enter") {
actionRef.value.click();
}
}
function handleKeyup(event) {
if (!isFocusing.value || event.key !== " ") {
return;
}
event.preventDefault();
actionRef.value.click();
}
function preview(varFile) {
const { disabled, previewed, preventDefaultPreview, onPreview } = props2;
if ((form == null ? void 0 : form.disabled.value) || disabled || !previewed) {
return;
}
call(onPreview, reactive(varFile));
if (preventDefaultPreview) {
return;
}
const { url } = varFile;
if (isHTMLSupportImage(url)) {
ImagePreview(url);
return;
}
if (isHTMLSupportVideo(url)) {
currentPreview.value = varFile;
showPreview.value = true;
}
}
function createVarFile(file) {
return {
id: fid++,
url: "",
cover: "",
name: file.name,
file,
progress: 0
};
}
function getFiles(event) {
const el = event.target;
const { files: fileList } = el;
return Array.from(fileList);
}
function resolver(varFile) {
return __async(this, null, function* () {
const file = varFile.file;
const shouldWithDataURL = props2.resolveType === "default" && file.type.startsWith("image") || props2.resolveType === "data-url";
if (shouldWithDataURL) {
const dataURL = yield toDataURL(file);
varFile.cover = dataURL;
varFile.url = dataURL;
}
return varFile;
});
}
function getResolvers(varFiles) {
return varFiles.map(resolver);
}
function getBeforeReaders(varFiles) {
const { onBeforeRead } = props2;
return varFiles.map(
(varFile) => new Promise((resolve) => {
if (!onBeforeRead) {
resolve({
valid: true,
varFile
});
}
const results = normalizeToArray(call(onBeforeRead, reactive(varFile)));
Promise.all(results).then((values) => {
resolve({
valid: values.every(Boolean),
varFile
});
});
})
);
}
function handleChange(event) {
return __async(this, null, function* () {
const { maxsize, maxlength, modelValue, onOversize, onAfterRead, onBeforeFilter, readonly, disabled } = props2;
if ((form == null ? void 0 : form.disabled.value) || (form == null ? void 0 : form.readonly.value) || disabled || readonly) {
return;
}
const getValidSizeVarFile = (varFiles2) => varFiles2.filter((varFile) => {
if (varFile.file.size > toNumber(maxsize)) {
call(onOversize, reactive(varFile));
return false;
}
return true;
});
const getValidLengthVarFiles = (varFiles2) => {
const limit = Math.min(varFiles2.length, toNumber(maxlength) - modelValue.length);
return varFiles2.slice(0, limit);
};
const getFilterVarFiles = (varFiles2) => __async(this, null, function* () {
if (!onBeforeFilter) {
return varFiles2;
}
const events = normalizeToArray(onBeforeFilter);
for (const event2 of events) {
varFiles2 = yield event2(varFiles2);
}
return varFiles2;
});
const files2 = getFiles(event);
let varFiles = files2.map(createVarFile);
varFiles = yield getFilterVarFiles(varFiles);
varFiles = maxsize != null ? getValidSizeVarFile(varFiles) : varFiles;
varFiles = maxlength != null ? getValidLengthVarFiles(varFiles) : varFiles;
const resolvedVarFiles = yield Promise.all(getResolvers(varFiles));
const validationVarFiles = yield Promise.all(getBeforeReaders(resolvedVarFiles));
const validVarFiles = validationVarFiles.filter(({ valid }) => valid).map(({ varFile }) => varFile);
call(props2["onUpdate:modelValue"], [...modelValue, ...validVarFiles]);
event.target.value = "";
validVarFiles.forEach((varFile) => call(onAfterRead, reactive(varFile)));
});
}
function handleRemove(removedVarFile) {
return __async(this, null, function* () {
const { disabled, readonly, modelValue, onBeforeRemove, onRemove } = props2;
if ((form == null ? void 0 : form.disabled.value) || (form == null ? void 0 : form.readonly.value) || disabled || readonly) {
return;
}
if (onBeforeRemove) {
const results = normalizeToArray(call(onBeforeRemove, reactive(removedVarFile)));
if ((yield Promise.all(results)).some((result) => !result)) {
return;
}
}
const expectedFiles = modelValue.filter((varFile) => varFile !== removedVarFile);
call(onRemove, reactive(removedVarFile));
validateWithTrigger("onRemove");
call(props2["onUpdate:modelValue"], expectedFiles);
});
}
function handleActionClick(event) {
if ((form == null ? void 0 : form.disabled.value) || props2.disabled) {
return;
}
if (props2.onClickAction) {
call(props2.onClickAction, chooseFile, event);
return;
}
chooseFile();
}
function getSuccess() {
return props2.modelValue.filter((varFile) => varFile.state === "success");
}
function getError() {
return props2.modelValue.filter((varFile) => varFile.state === "error");
}
function getLoading() {
return props2.modelValue.filter((varFile) => varFile.state === "loading");
}
function chooseFile() {
input.value.click();
}
function closePreview() {
currentPreview.value = null;
showPreview.value = false;
ImagePreview.close();
}
function validateWithTrigger(trigger) {
nextTick(() => {
const { validateTrigger, rules, modelValue } = props2;
vt(validateTrigger, trigger, rules, modelValue, varFileUtils);
});
}
function validate() {
return v(props2.rules, props2.modelValue, varFileUtils);
}
function reset() {
callReset = true;
call(props2["onUpdate:modelValue"], []);
resetValidation();
}
return {
input,
actionRef,
files,
showPreview,
currentPreview,
errorMessage,
maxlengthText,
hovering,
isFocusing,
formDisabled: form == null ? void 0 : form.disabled,
formReadonly: form == null ? void 0 : form.readonly,
n,
classes,
formatElevation,
toNumber,
handleHovering,
isHTMLSupportVideo,
isHTMLSupportImage,
preview,
handleChange,
handleRemove,
getSuccess,
getError,
getLoading,
validate,
resetValidation,
reset,
chooseFile,
closePreview,
handleActionClick,
toSizeUnit
};
}
});
__sfc__.render = __render__;
var stdin_default = __sfc__;
export {
stdin_default as default
};