UNPKG

winbox-ui-next

Version:

We Design Vue 2.0: A Vue.js 3 UI Library

320 lines (319 loc) 10.5 kB
var __defProp = Object.defineProperty; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; import { defineComponent, toRefs, ref, computed, onMounted, watch, nextTick, createVNode, mergeProps, createTextVNode, isVNode } from "vue"; import Textarea from "../textarea/index.js"; import Input from "../input/index.js"; import Trigger from "../trigger/index.js"; import DropdownPanel from "../_components/dropdown/dropdown-panel.js"; import DropDownOption from "../_components/dropdown/dropdown-option.js"; import "../_components/dropdown/dropdown-optgroup.js"; import { useOptions } from "../_hooks/use-options.js"; import { getTextBeforeSelection, getLastMeasureIndex, isValidSearch } from "./utils.js"; import { getKeyDownHandler, CODE } from "../_utils/keyboard.js"; import { getPrefixCls } from "../_utils/global-config.js"; import { getSizeStyles } from "../textarea/utils.js"; import ResizeObserver from "../_components/resize-observer.js"; import { isFunction } from "../_utils/is.js"; function _isSlot(s) { return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s); } var _Mention = defineComponent({ name: "Mention", inheritAttrs: false, props: { modelValue: String, defaultValue: { type: String, default: "" }, data: { type: Array, default: () => [] }, prefix: { type: [String, Array], default: "@" }, split: { type: String, default: " " }, type: { type: String, default: "input" }, onChange: { type: [Function, Array] }, onSelect: { type: [Function, Array] } }, emits: [ "update:modelValue", "change", "search", "select" ], setup(props, { emit, attrs, slots }) { const prefixCls = getPrefixCls("mention"); let styleDeclaration; const { data } = toRefs(props); const dropdownRef = ref(); const optionRefs = ref({}); const _value = ref(props.defaultValue); const computeValue = computed(() => { var _a; return (_a = props.modelValue) != null ? _a : _value.value; }); const measureInfo = ref({ measuring: false, location: -1, prefix: "", text: "" }); const resetMeasureInfo = () => { measureInfo.value = { measuring: false, location: -1, prefix: "", text: "" }; }; const inputRef = ref(); const measureText = computed(() => measureInfo.value.text); const filterOption = ref(true); const handleInput = (value, e) => { const text = getTextBeforeSelection(e.target); const lastMeasure = getLastMeasureIndex(text, props.prefix); if (lastMeasure.location > -1) { const measureText2 = text.slice(lastMeasure.location + lastMeasure.prefix.length); if (isValidSearch(measureText2, props.split)) { _popupVisible.value = true; measureInfo.value = __spreadValues({ measuring: true, text: measureText2 }, lastMeasure); emit("search", measureText2); } else if (measureInfo.value.location > -1) { resetMeasureInfo(); } } else if (measureInfo.value.location > -1) { resetMeasureInfo(); } _value.value = value; emit("update:modelValue", value); emit("change", value); }; const _popupVisible = ref(false); const computedPopupVisible = computed(() => _popupVisible.value && measureInfo.value.measuring && nodes.value.length > 0); const handleResize = () => { mirrorStyle.value = getSizeStyles(styleDeclaration); }; const handlePopupVisibleChange = (popupVisible) => { _popupVisible.value = popupVisible; }; const extraOptions = ref([]); const { nodes, activeOption, getNextActiveOption, scrollIntoView, enabledOptionSet, optionInfoMap } = useOptions({ options: data, extraOptions, inputValue: measureText, filterOption, dropdownRef, optionRefs }); const handleSelect = (value, e) => { const measureStart = measureInfo.value.location; const measureEnd = measureInfo.value.location + measureInfo.value.text.length; let head = _value.value.slice(0, measureStart); let tail = _value.value.slice(measureEnd + 1); head += !head || head.endsWith(props.split) || head.endsWith("\n") ? "" : props.split; tail = (!tail || tail.startsWith(props.split) || tail.startsWith("\n") ? "" : props.split) + tail; const match = `${measureInfo.value.prefix}${value}`; const nextValue = `${head}${match}${tail}`; _value.value = nextValue; emit("select", value); emit("update:modelValue", value); emit("change", nextValue); resetMeasureInfo(); }; const handleMouseEnter = (value, e) => { const optionInfo = optionInfoMap.get(value); if (optionInfo) { activeOption.value = optionInfo; } }; const handleMouseLeave = (e) => { activeOption.value = void 0; }; const mirrorStyle = ref(); onMounted(() => { var _a; if (props.type === "textarea" && ((_a = inputRef.value) == null ? void 0 : _a.textareaRef)) { styleDeclaration = window.getComputedStyle(inputRef.value.textareaRef); mirrorStyle.value = getSizeStyles(styleDeclaration); } }); const handleKeyDown = getKeyDownHandler(new Map([[CODE.ENTER, (e) => { if (computedPopupVisible.value) { if (activeOption.value) { handleSelect(activeOption.value.value); } e.preventDefault(); } }], [CODE.ESC, (e) => { handlePopupVisibleChange(false); e.preventDefault(); }], [CODE.ARROW_DOWN, (e) => { if (computedPopupVisible.value) { const next = getNextActiveOption("down"); if (next) { activeOption.value = next; scrollIntoView(next.value); } e.preventDefault(); } }], [CODE.ARROW_UP, (e) => { if (computedPopupVisible.value) { const next = getNextActiveOption("up"); if (next) { activeOption.value = next; scrollIntoView(next.value); } e.preventDefault(); } }]])); const getOptionContentFunc = (item) => { if (isFunction(slots.option) && item.value) { const optionInfo = optionInfoMap.get(item.value); const optionSlot = slots.option; return () => optionSlot({ data: optionInfo }); } return () => item.label; }; const renderOption = (item) => { const { value = "" } = item; return createVNode(DropDownOption, { "ref": (ref2) => { if (ref2 == null ? void 0 : ref2.$el) { optionRefs.value[value] = ref2.$el; } }, "key": item.key, "value": value, "disabled": item.disabled, "isActive": activeOption.value && value === activeOption.value.value, "onClick": handleSelect, "onMouseenter": handleMouseEnter, "onMouseleave": handleMouseLeave }, { default: getOptionContentFunc(item) }); }; const renderDropdown = () => { if (!measureInfo.value.measuring || nodes.value.length === 0) { return null; } const _children = nodes.value.map((node) => renderOption(node)); return createVNode(DropdownPanel, { "ref": dropdownRef }, _isSlot(_children) ? _children : { default: () => [_children] }); }; const mirrorRef = ref(); watch(computedPopupVisible, (visible) => { if (props.type === "textarea" && visible) { nextTick(() => { var _a, _b; if (((_a = inputRef.value) == null ? void 0 : _a.textareaRef) && inputRef.value.textareaRef.scrollTop > 0) { (_b = mirrorRef.value) == null ? void 0 : _b.scrollTo(0, inputRef.value.textareaRef.scrollTop); } }); } }); const render = () => { var _a; if (props.type === "textarea") { return createVNode("div", { "class": prefixCls }, [createVNode(ResizeObserver, { "onResize": handleResize }, { default: () => [createVNode(Textarea, mergeProps(attrs, { "ref": inputRef, "modelValue": computeValue.value, "onInput": handleInput, "onKeydown": handleKeyDown }), null)] }), measureInfo.value.measuring && nodes.value.length > 0 && createVNode("div", { "ref": mirrorRef, "style": mirrorStyle.value, "class": `${prefixCls}-measure` }, [(_a = computeValue.value) == null ? void 0 : _a.slice(0, measureInfo.value.location), createVNode(Trigger, { "trigger": "focus", "position": "bl", "popupOffset": 4, "preventFocus": true, "popupVisible": computedPopupVisible.value, "clickToClose": false, "onPopupVisibleChange": handlePopupVisibleChange }, { default: () => [createVNode("span", null, [createTextVNode("@")])], content: renderDropdown })])]); } return createVNode(Trigger, { "trigger": "focus", "position": "bl", "popupOffset": 4, "preventFocus": true, "popupVisible": computedPopupVisible.value, "clickToClose": false, "autoFitPopupWidth": true, "onPopupVisibleChange": handlePopupVisibleChange }, { default: () => [createVNode(Input, mergeProps(attrs, { "ref": inputRef, "modelValue": computeValue.value, "onInput": handleInput, "onKeydown": handleKeyDown }), slots)], content: renderDropdown }); }; return render; } }); export { _Mention as default };