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