ant-design-vue
Version:
An enterprise-class UI design language and Vue-based implementation
282 lines • 10.2 kB
JavaScript
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _extends from "@babel/runtime/helpers/esm/extends";
var _excluded = ["prefixCls", "placement", "transitionName", "getPopupContainer", "direction"],
_excluded2 = ["class", "style"];
import { resolveDirective as _resolveDirective, createVNode as _createVNode } from "vue";
import { toRef, watchEffect, defineComponent, provide, withDirectives, ref, reactive, onUpdated, nextTick, computed } from 'vue';
import classNames from '../../_util/classNames';
import KeyCode from '../../_util/KeyCode';
import { initDefaultProps } from '../../_util/props-util';
import { getBeforeSelectionText, getLastMeasureIndex, replaceWithMeasure, setInputSelection } from './util';
import KeywordTrigger from './KeywordTrigger';
import { vcMentionsProps, defaultProps } from './mentionsProps';
import MentionsContextKey from './MentionsContext';
import antInputDirective from '../../_util/antInputDirective';
import omit from '../../_util/omit';
function noop() {}
export default defineComponent({
compatConfig: {
MODE: 3
},
name: 'Mentions',
inheritAttrs: false,
props: initDefaultProps(vcMentionsProps, defaultProps),
slots: ['notFoundContent', 'option'],
emits: ['change', 'select', 'search', 'focus', 'blur', 'pressenter'],
setup: function setup(props, _ref) {
var emit = _ref.emit,
attrs = _ref.attrs,
expose = _ref.expose,
slots = _ref.slots;
var measure = ref(null);
var textarea = ref(null);
var focusId = ref();
var state = reactive({
value: props.value || '',
measuring: false,
measureLocation: 0,
measureText: null,
measurePrefix: '',
activeIndex: 0,
isFocus: false
});
watchEffect(function () {
state.value = props.value;
});
var triggerChange = function triggerChange(val) {
emit('change', val);
};
var onChange = function onChange(_ref2) {
var _ref2$target = _ref2.target,
value = _ref2$target.value,
composing = _ref2$target.composing,
isComposing = _ref2.isComposing;
if (isComposing || composing) return;
triggerChange(value);
};
var startMeasure = function startMeasure(measureText, measurePrefix, measureLocation) {
_extends(state, {
measuring: true,
measureText: measureText,
measurePrefix: measurePrefix,
measureLocation: measureLocation,
activeIndex: 0
});
};
var stopMeasure = function stopMeasure(callback) {
_extends(state, {
measuring: false,
measureLocation: 0,
measureText: null
});
callback === null || callback === void 0 ? void 0 : callback();
};
var onKeyDown = function onKeyDown(event) {
var which = event.which;
// Skip if not measuring
if (!state.measuring) {
return;
}
if (which === KeyCode.UP || which === KeyCode.DOWN) {
// Control arrow function
var optionLen = options.value.length;
var offset = which === KeyCode.UP ? -1 : 1;
var newActiveIndex = (state.activeIndex + offset + optionLen) % optionLen;
state.activeIndex = newActiveIndex;
event.preventDefault();
} else if (which === KeyCode.ESC) {
stopMeasure();
} else if (which === KeyCode.ENTER) {
// Measure hit
event.preventDefault();
if (!options.value.length) {
stopMeasure();
return;
}
var option = options.value[state.activeIndex];
selectOption(option);
}
};
var onKeyUp = function onKeyUp(event) {
var key = event.key,
which = event.which;
var prevMeasureText = state.measureText,
measuring = state.measuring;
var prefix = props.prefix,
validateSearch = props.validateSearch;
var target = event.target;
if (target.composing) {
return;
}
var selectionStartText = getBeforeSelectionText(target);
var _getLastMeasureIndex = getLastMeasureIndex(selectionStartText, prefix),
measureIndex = _getLastMeasureIndex.location,
measurePrefix = _getLastMeasureIndex.prefix;
// Skip if match the white key list
if ([KeyCode.ESC, KeyCode.UP, KeyCode.DOWN, KeyCode.ENTER].indexOf(which) !== -1) {
return;
}
if (measureIndex !== -1) {
var measureText = selectionStartText.slice(measureIndex + measurePrefix.length);
var validateMeasure = validateSearch(measureText, props);
var matchOption = !!getOptions(measureText).length;
if (validateMeasure) {
if (key === measurePrefix || key === 'Shift' || measuring || measureText !== prevMeasureText && matchOption) {
startMeasure(measureText, measurePrefix, measureIndex);
}
} else if (measuring) {
// Stop if measureText is invalidate
stopMeasure();
}
/**
* We will trigger `onSearch` to developer since they may use for async update.
* If met `space` means user finished searching.
*/
if (validateMeasure) {
emit('search', measureText, measurePrefix);
}
} else if (measuring) {
stopMeasure();
}
};
var onPressEnter = function onPressEnter(event) {
if (!state.measuring) {
emit('pressenter', event);
}
};
var onInputFocus = function onInputFocus(event) {
onFocus(event);
};
var onInputBlur = function onInputBlur(event) {
onBlur(event);
};
var onFocus = function onFocus(event) {
clearTimeout(focusId.value);
var isFocus = state.isFocus;
if (!isFocus && event) {
emit('focus', event);
}
state.isFocus = true;
};
var onBlur = function onBlur(event) {
focusId.value = setTimeout(function () {
state.isFocus = false;
stopMeasure();
emit('blur', event);
}, 100);
};
var selectOption = function selectOption(option) {
var split = props.split;
var _option$value = option.value,
mentionValue = _option$value === void 0 ? '' : _option$value;
var _replaceWithMeasure = replaceWithMeasure(state.value, {
measureLocation: state.measureLocation,
targetText: mentionValue,
prefix: state.measurePrefix,
selectionStart: textarea.value.selectionStart,
split: split
}),
text = _replaceWithMeasure.text,
selectionLocation = _replaceWithMeasure.selectionLocation;
triggerChange(text);
stopMeasure(function () {
// We need restore the selection position
setInputSelection(textarea.value, selectionLocation);
});
emit('select', option, state.measurePrefix);
};
var setActiveIndex = function setActiveIndex(activeIndex) {
state.activeIndex = activeIndex;
};
var getOptions = function getOptions(measureText) {
var targetMeasureText = measureText || state.measureText || '';
var filterOption = props.filterOption;
var list = props.options.filter(function (option) {
/** Return all result if `filterOption` is false. */
if (!!filterOption === false) {
return true;
}
return filterOption(targetMeasureText, option);
});
return list;
};
var options = computed(function () {
return getOptions();
});
var focus = function focus() {
textarea.value.focus();
};
var blur = function blur() {
textarea.value.blur();
};
expose({
blur: blur,
focus: focus
});
provide(MentionsContextKey, {
activeIndex: toRef(state, 'activeIndex'),
setActiveIndex: setActiveIndex,
selectOption: selectOption,
onFocus: onFocus,
onBlur: onBlur,
loading: toRef(props, 'loading')
});
onUpdated(function () {
nextTick(function () {
if (state.measuring) {
measure.value.scrollTop = textarea.value.scrollTop;
}
});
});
return function () {
var measureLocation = state.measureLocation,
measurePrefix = state.measurePrefix,
measuring = state.measuring;
var prefixCls = props.prefixCls,
placement = props.placement,
transitionName = props.transitionName,
getPopupContainer = props.getPopupContainer,
direction = props.direction,
restProps = _objectWithoutProperties(props, _excluded);
var className = attrs.class,
style = attrs.style,
otherAttrs = _objectWithoutProperties(attrs, _excluded2);
var inputProps = omit(restProps, ['value', 'prefix', 'split', 'validateSearch', 'filterOption', 'options', 'loading']);
var textareaProps = _objectSpread(_objectSpread(_objectSpread({}, inputProps), otherAttrs), {}, {
onChange: noop,
onSelect: noop,
value: state.value,
onInput: onChange,
onBlur: onInputBlur,
onKeydown: onKeyDown,
onKeyup: onKeyUp,
onFocus: onInputFocus,
onPressenter: onPressEnter
});
return _createVNode("div", {
"class": classNames(prefixCls, className),
"style": style
}, [withDirectives(_createVNode("textarea", _objectSpread({
"ref": textarea
}, textareaProps), null), [[antInputDirective]]), measuring && _createVNode("div", {
"ref": measure,
"class": "".concat(prefixCls, "-measure")
}, [state.value.slice(0, measureLocation), _createVNode(KeywordTrigger, {
"prefixCls": prefixCls,
"transitionName": transitionName,
"placement": placement,
"options": measuring ? options.value : [],
"visible": true,
"direction": direction,
"getPopupContainer": getPopupContainer
}, {
default: function _default() {
return [_createVNode("span", null, [measurePrefix])];
},
notFoundContent: slots.notFoundContent,
option: slots.option
}), state.value.slice(measureLocation + measurePrefix.length)])]);
};
}
});