UNPKG

@varlet/ui

Version:

A Vue3 component library based on Material Design 2 and 3, supporting mobile and desktop.

527 lines (526 loc) • 20.6 kB
import { computed, defineComponent, nextTick, ref, watch } from "vue"; import { assert, call, doubleRaf, isArray, isEmpty, isFunction, preventDefault } from "@varlet/shared"; import { useEventListener } from "@varlet/use"; import VarChip from "../chip/index.mjs"; import VarFieldDecorator from "../field-decorator/index.mjs"; import VarFormDetails from "../form-details/index.mjs"; import { useForm } from "../form/provide.mjs"; import VarIcon from "../icon/index.mjs"; import VarMenu from "../menu/index.mjs"; import VarOption from "../option/index.mjs"; import { createNamespace, MaybeVNode, useValidation } from "../utils/components.mjs"; import { focusChildElementByKey, toPxNum } from "../utils/elements.mjs"; import { props } from "./props.mjs"; import { useOptions } from "./provide.mjs"; import { useSelectController } from "./useSelectController.mjs"; const { name, n, classes } = createNamespace("select"); import { renderSlot as _renderSlot, renderList as _renderList, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock, resolveComponent as _resolveComponent, createVNode as _createVNode, withModifiers as _withModifiers, normalizeClass as _normalizeClass, withCtx as _withCtx, createBlock as _createBlock, createCommentVNode as _createCommentVNode, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createElementVNode as _createElementVNode, normalizeStyle as _normalizeStyle, normalizeProps as _normalizeProps, guardReactiveProps as _guardReactiveProps, createSlots as _createSlots } from "vue"; const _hoisted_1 = ["tabindex"]; function __render__(_ctx, _cache) { const _component_maybe_v_node = _resolveComponent("maybe-v-node"); const _component_var_chip = _resolveComponent("var-chip"); const _component_var_icon = _resolveComponent("var-icon"); const _component_var_field_decorator = _resolveComponent("var-field-decorator"); const _component_var_option = _resolveComponent("var-option"); const _component_var_menu = _resolveComponent("var-menu"); const _component_var_form_details = _resolveComponent("var-form-details"); return _openBlock(), _createElementBlock("div", { ref: "root", class: _normalizeClass(_ctx.n()), tabindex: _ctx.disabled || _ctx.formDisabled ? void 0 : "0", onFocus: _cache[3] || (_cache[3] = (...args) => _ctx.handleFocus && _ctx.handleFocus(...args)), onBlur: _cache[4] || (_cache[4] = (...args) => _ctx.handleRootBlur && _ctx.handleRootBlur(...args)) }, [ _createVNode(_component_var_menu, { show: _ctx.showMenu, "onUpdate:show": _cache[1] || (_cache[1] = ($event) => _ctx.showMenu = $event), "var-select-cover": "", "same-width": "", "close-on-click-reference": "", "close-on-key-escape": false, class: _normalizeClass(_ctx.n("menu")), "popover-class": _ctx.variant === "standard" && _ctx.hint ? _ctx.n("--standard-menu-margin") : void 0, "offset-y": _ctx.offsetY, disabled: _ctx.formReadonly || _ctx.readonly || _ctx.formDisabled || _ctx.disabled, placement: _ctx.placement, "default-style": false, onClickOutside: _ctx.handleClickOutside }, { menu: _withCtx(() => [ _createElementVNode( "div", { ref: "menuEl", class: _normalizeClass(_ctx.classes(_ctx.n("scroller"), _ctx.n("$-elevation--3"))) }, [ _ctx.options.length ? (_openBlock(true), _createElementBlock( _Fragment, { key: 0 }, _renderList(_ctx.options, (option) => { return _openBlock(), _createBlock(_component_var_option, { key: option[_ctx.valueKey], label: option[_ctx.labelKey], value: option[_ctx.valueKey], option, disabled: option.disabled, ripple: option.ripple }, null, 8, ["label", "value", "option", "disabled", "ripple"]); }), 128 /* KEYED_FRAGMENT */ )) : _createCommentVNode("v-if", true), _renderSlot(_ctx.$slots, "default") ], 2 /* CLASS */ ) ]), default: _withCtx(() => [ _createVNode( _component_var_field_decorator, _normalizeProps(_guardReactiveProps({ value: _ctx.modelValue, size: _ctx.size, variant: _ctx.variant, placeholder: _ctx.placeholder, line: _ctx.line, hint: _ctx.hint, textColor: _ctx.textColor, focusColor: _ctx.focusColor, blurColor: _ctx.blurColor, isFocusing: _ctx.isFocusing, isError: !!_ctx.errorMessage, formDisabled: _ctx.formDisabled, disabled: _ctx.disabled, clearable: _ctx.clearable, cursor: _ctx.cursor, onClick: _ctx.handleClick, onClear: _ctx.handleClear })), _createSlots({ "clear-icon": _withCtx(({ clear }) => [ _renderSlot(_ctx.$slots, "clear-icon", { clear }) ]), "append-icon": _withCtx(() => [ _renderSlot(_ctx.$slots, "append-icon") ]), default: _withCtx(() => [ _createElementVNode( "div", { class: _normalizeClass(_ctx.classes(_ctx.n("select"), [_ctx.errorMessage, _ctx.n("--error")], [_ctx.formDisabled || _ctx.disabled, _ctx.n("--disabled")])), style: _normalizeStyle({ textAlign: _ctx.textAlign, color: _ctx.textColor }) }, [ _createElementVNode( "div", { class: _normalizeClass(_ctx.n("label")) }, [ !_ctx.isEmptyModelValue ? _renderSlot(_ctx.$slots, "selected", { key: 0 }, () => [ _ctx.multiple ? (_openBlock(), _createElementBlock( _Fragment, { key: 0 }, [ _ctx.chip ? (_openBlock(), _createElementBlock( "div", { key: 0, class: _normalizeClass(_ctx.n("chips")) }, [ (_openBlock(true), _createElementBlock( _Fragment, null, _renderList(_ctx.labels, (l) => { return _openBlock(), _createBlock(_component_var_chip, { key: l, class: _normalizeClass(_ctx.n("chip")), "var-select-cover": "", closeable: "", size: "small", type: _ctx.errorMessage ? "danger" : void 0, onClick: _cache[0] || (_cache[0] = _withModifiers(() => { }, ["stop"])), onClose: () => _ctx.handleClose(l) }, { default: _withCtx(() => [ _createVNode(_component_maybe_v_node, { is: l }, null, 8, ["is"]) ]), _: 2 /* DYNAMIC */ }, 1032, ["class", "type", "onClose"]); }), 128 /* KEYED_FRAGMENT */ )) ], 2 /* CLASS */ )) : (_openBlock(), _createElementBlock( "div", { key: 1, class: _normalizeClass(_ctx.n("values")) }, [ (_openBlock(true), _createElementBlock( _Fragment, null, _renderList(_ctx.labels, (l, labelIndex) => { return _openBlock(), _createElementBlock( _Fragment, { key: l }, [ _createVNode(_component_maybe_v_node, { is: l }, null, 8, ["is"]), _createTextVNode( _toDisplayString(labelIndex !== _ctx.labels.length - 1 ? _ctx.separator : ""), 1 /* TEXT */ ) ], 64 /* STABLE_FRAGMENT */ ); }), 128 /* KEYED_FRAGMENT */ )) ], 2 /* CLASS */ )) ], 64 /* STABLE_FRAGMENT */ )) : (_openBlock(), _createBlock(_component_maybe_v_node, { key: 1, is: _ctx.label }, null, 8, ["is"])) ]) : _createCommentVNode("v-if", true) ], 2 /* CLASS */ ), _ctx.enableCustomPlaceholder ? (_openBlock(), _createElementBlock( "span", { key: 0, class: _normalizeClass(_ctx.classes(_ctx.n("placeholder"), _ctx.n("$--ellipsis"))), style: _normalizeStyle({ color: _ctx.placeholderColor }) }, _toDisplayString(_ctx.placeholder), 7 /* TEXT, CLASS, STYLE */ )) : _createCommentVNode("v-if", true), _renderSlot(_ctx.$slots, "arrow-icon", { focus: _ctx.isFocusing, menuOpen: _ctx.showMenu }, () => [ _createVNode(_component_var_icon, { class: _normalizeClass(_ctx.classes(_ctx.n("arrow"), [_ctx.showMenu, _ctx.n("--arrow-rotate")])), "var-select-cover": "", name: "menu-down", transition: 300 }, null, 8, ["class"]) ]) ], 6 /* CLASS, STYLE */ ) ]), _: 2 /* DYNAMIC */ }, [ _ctx.$slots["prepend-icon"] ? { name: "prepend-icon", fn: _withCtx(() => [ _renderSlot(_ctx.$slots, "prepend-icon") ]), key: "0" } : void 0 ]), 1040 /* FULL_PROPS, DYNAMIC_SLOTS */ ) ]), _: 3 /* FORWARDED */ }, 8, ["show", "class", "popover-class", "offset-y", "disabled", "placement", "onClickOutside"]), _createVNode(_component_var_form_details, { "error-message": _ctx.errorMessage, onClick: _cache[2] || (_cache[2] = _withModifiers(() => { }, ["stop"])) }, null, 8, ["error-message"]) ], 42, _hoisted_1); } const __sfc__ = defineComponent({ name, components: { VarIcon, VarMenu, VarChip, VarOption, VarFieldDecorator, VarFormDetails, MaybeVNode }, props, setup(props2) { const isFocusing = ref(false); const showMenu = ref(false); const root = ref(null); const multiple = computed(() => props2.multiple); const focusColor = computed(() => props2.focusColor); const isEmptyModelValue = computed(() => isEmpty(props2.modelValue)); const cursor = computed(() => props2.disabled || props2.readonly ? "" : "pointer"); const offsetY = ref(0); const { bindForm, form } = useForm(); const { length, options, bindOptions } = useOptions(); const { label, labels, computeLabel, getSelectedValue } = useSelectController({ modelValue: () => props2.modelValue, multiple: () => props2.multiple, optionProviders: () => options, optionProvidersLength: () => length.value }); const { errorMessage, validateWithTrigger: vt, validate: v, // expose resetValidation } = useValidation(); const menuEl = ref(null); const placement = computed(() => props2.variant === "outlined" ? "bottom" : "cover-top"); const placeholderColor = computed(() => { const { hint, blurColor, focusColor: focusColor2 } = props2; if (hint) { return void 0; } if (errorMessage.value) { return "var(--field-decorator-error-color)"; } if (isFocusing.value) { return focusColor2 || "var(--field-decorator-focus-color)"; } return blurColor || "var(--field-decorator-placeholder-color, var(--field-decorator-blur-color))"; }); const enableCustomPlaceholder = computed(() => !props2.hint && isEmpty(props2.modelValue)); const selectProvider = { multiple, focusColor, computeLabel, onSelect, reset, validate, resetValidation }; watch( () => props2.multiple, () => { assert( props2.multiple && isArray(props2.modelValue), "Select", "The modelValue must be an array when multiple is true" ); } ); bindOptions(selectProvider); useEventListener(() => window, "keydown", handleKeydown); useEventListener(() => window, "keyup", handleKeyup); call(bindForm, selectProvider); function handleKeydown(event) { const { disabled, readonly } = props2; if ((form == null ? void 0 : form.disabled.value) || (form == null ? void 0 : form.readonly.value) || disabled || readonly || !isFocusing.value) { return; } const { key } = event; if (key === " " && !showMenu.value) { preventDefault(event); return; } if (key === "Escape" && showMenu.value) { root.value.focus(); preventDefault(event); showMenu.value = false; return; } if (key === "Tab" && showMenu.value) { root.value.focus(); preventDefault(event); handleBlur(); return; } if (key === "Enter" && !showMenu.value) { preventDefault(event); showMenu.value = true; return; } if ((key === "ArrowDown" || key === "ArrowUp") && showMenu.value) { preventDefault(event); focusChildElementByKey(root.value, menuEl.value, key); } } function handleKeyup(event) { const { disabled, readonly } = props2; if ((form == null ? void 0 : form.disabled.value) || (form == null ? void 0 : form.readonly.value) || disabled || readonly || showMenu.value || !isFocusing.value) { return; } const { key } = event; if (key === " " && !showMenu.value) { preventDefault(event); showMenu.value = true; } } function validateWithTrigger(trigger) { nextTick(() => { const { validateTrigger, rules, modelValue } = props2; vt(validateTrigger, trigger, rules, modelValue); }); } function handleFocus() { const { disabled, readonly, onFocus } = props2; if ((form == null ? void 0 : form.disabled.value) || (form == null ? void 0 : form.readonly.value) || disabled || readonly) { return; } offsetY.value = toPxNum(props2.offsetY); focus(); call(onFocus); validateWithTrigger("onFocus"); } function handleBlur() { const { disabled, readonly, onBlur } = props2; if ((form == null ? void 0 : form.disabled.value) || (form == null ? void 0 : form.readonly.value) || disabled || readonly) { return; } blur(); call(onBlur); validateWithTrigger("onBlur"); } function handleRootBlur() { if (showMenu.value) { return; } handleBlur(); } function handleClickOutside() { if (!isFocusing.value) { return; } handleBlur(); } function onSelect(option) { const { disabled, readonly, multiple: multiple2, onChange } = props2; if ((form == null ? void 0 : form.disabled.value) || (form == null ? void 0 : form.readonly.value) || disabled || readonly) { return; } const selectedValue = getSelectedValue(option); call(props2["onUpdate:modelValue"], selectedValue); call(onChange, selectedValue); validateWithTrigger("onChange"); if (!multiple2) { root.value.focus(); doubleRaf().then(() => { showMenu.value = false; }); } } function handleClear() { const { disabled, readonly, multiple: multiple2, clearable, onClear, onChange } = props2; if ((form == null ? void 0 : form.disabled.value) || (form == null ? void 0 : form.readonly.value) || disabled || readonly || !clearable) { return; } const changedModelValue = multiple2 ? [] : void 0; call(props2["onUpdate:modelValue"], changedModelValue); call(onClear, changedModelValue); validateWithTrigger("onClear"); call(onChange, changedModelValue); validateWithTrigger("onChange"); } function handleClick(e) { const { disabled, onClick } = props2; if ((form == null ? void 0 : form.disabled.value) || disabled) { return; } call(onClick, e); validateWithTrigger("onClick"); } function handleClose(text) { const { disabled, readonly, modelValue, onClose, onChange } = props2; if ((form == null ? void 0 : form.disabled.value) || (form == null ? void 0 : form.readonly.value) || disabled || readonly) { return; } const option = options.find(({ label: label2 }) => label2.value === text); const changedModelValue = modelValue.filter( (value) => { var _a; return value !== ((_a = option.value.value) != null ? _a : option.label.value); } ); call(props2["onUpdate:modelValue"], changedModelValue); call(onClose, changedModelValue); validateWithTrigger("onClose"); call(onChange, changedModelValue); validateWithTrigger("onChange"); } function focus() { offsetY.value = toPxNum(props2.offsetY); isFocusing.value = true; } function blur() { isFocusing.value = false; showMenu.value = false; } function validate() { return v(props2.rules, props2.modelValue); } function reset() { call(props2["onUpdate:modelValue"], props2.multiple ? [] : void 0); resetValidation(); } return { root, offsetY, isFocusing, showMenu, errorMessage, formDisabled: form == null ? void 0 : form.disabled, formReadonly: form == null ? void 0 : form.readonly, label, labels, isEmptyModelValue, menuEl, placement, cursor, placeholderColor, enableCustomPlaceholder, isFunction, n, classes, handleFocus, handleBlur, handleClickOutside, handleClear, handleClick, handleClose, handleRootBlur, reset, validate, resetValidation, focus, blur }; } }); __sfc__.render = __render__; var stdin_default = __sfc__; export { stdin_default as default };