UNPKG

@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
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 };