winbox-ui-next
Version:
We Design Vue 2.0: A Vue.js 3 UI Library
320 lines (319 loc) • 10.5 kB
JavaScript
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 };