ant-design-vue
Version:
An enterprise-class UI design language and Vue-based implementation
352 lines (319 loc) • 11.2 kB
JavaScript
import _mergeJSXProps from 'babel-helper-vue-jsx-merge-props';
import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties';
import _extends from 'babel-runtime/helpers/extends';
import omit from 'omit.js';
import KeyCode from '../../_util/KeyCode';
import BaseMixin from '../../_util/BaseMixin';
import { getSlots, hasProp, getOptionProps, getListeners, initDefaultProps } from '../../_util/props-util';
import warning from 'warning';
import { getBeforeSelectionText, getLastMeasureIndex, replaceWithMeasure, setInputSelection } from './util';
import KeywordTrigger from './KeywordTrigger';
import { vcMentionsProps, defaultProps } from './mentionsProps';
function noop() {}
var Mentions = {
name: 'Mentions',
mixins: [BaseMixin],
inheritAttrs: false,
model: {
prop: 'value',
event: 'change'
},
props: initDefaultProps(vcMentionsProps, defaultProps),
provide: function provide() {
return {
mentionsContext: this
};
},
data: function data() {
var _$props = this.$props,
_$props$value = _$props.value,
value = _$props$value === undefined ? '' : _$props$value,
_$props$defaultValue = _$props.defaultValue,
defaultValue = _$props$defaultValue === undefined ? '' : _$props$defaultValue;
warning(this.$props.children, 'please children prop replace slots.default');
return {
_value: !hasProp(this, 'value') ? defaultValue : value,
measuring: false,
measureLocation: 0,
measureText: null,
measurePrefix: '',
activeIndex: 0,
isFocus: false
};
},
watch: {
value: function value(val) {
this.$data._value = val;
}
},
updated: function updated() {
var _this = this;
this.$nextTick(function () {
var measuring = _this.$data.measuring;
// Sync measure div top with textarea for rc-trigger usage
if (measuring) {
_this.$refs.measure.scrollTop = _this.$refs.textarea.scrollTop;
}
});
},
methods: {
triggerChange: function triggerChange(value) {
var props = getOptionProps(this);
if (!('value' in props)) {
this.setState({ _value: value });
} else {
this.$forceUpdate();
}
this.$emit('change', value);
},
onChange: function onChange(_ref) {
var _ref$target = _ref.target,
value = _ref$target.value,
composing = _ref$target.composing,
isComposing = _ref.isComposing;
if (isComposing || composing) return;
this.triggerChange(value);
},
onKeyDown: function onKeyDown(event) {
var which = event.which;
var _$data = this.$data,
activeIndex = _$data.activeIndex,
measuring = _$data.measuring;
// Skip if not measuring
if (!measuring) {
return;
}
if (which === KeyCode.UP || which === KeyCode.DOWN) {
// Control arrow function
var optionLen = this.getOptions().length;
var offset = which === KeyCode.UP ? -1 : 1;
var newActiveIndex = (activeIndex + offset + optionLen) % optionLen;
this.setState({
activeIndex: newActiveIndex
});
event.preventDefault();
} else if (which === KeyCode.ESC) {
this.stopMeasure();
} else if (which === KeyCode.ENTER) {
// Measure hit
event.preventDefault();
var options = this.getOptions();
if (!options.length) {
this.stopMeasure();
return;
}
var option = options[activeIndex];
this.selectOption(option);
}
},
/**
* When to start measure:
* 1. When user press `prefix`
* 2. When measureText !== prevMeasureText
* - If measure hit
* - If measuring
*
* When to stop measure:
* 1. Selection is out of range
* 2. Contains `space`
* 3. ESC or select one
*/
onKeyUp: function onKeyUp(event) {
var key = event.key,
which = event.which;
var _$data2 = this.$data,
prevMeasureText = _$data2.measureText,
measuring = _$data2.measuring;
var _$props2 = this.$props,
_$props2$prefix = _$props2.prefix,
prefix = _$props2$prefix === undefined ? '' : _$props2$prefix,
validateSearch = _$props2.validateSearch;
var target = event.target;
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, this.$props);
var matchOption = !!this.getOptions(measureText).length;
if (validateMeasure) {
if (key === measurePrefix || measuring || measureText !== prevMeasureText && matchOption) {
this.startMeasure(measureText, measurePrefix, measureIndex);
}
} else if (measuring) {
// Stop if measureText is invalidate
this.stopMeasure();
}
/**
* We will trigger `onSearch` to developer since they may use for async update.
* If met `space` means user finished searching.
*/
if (validateMeasure) {
this.$emit('search', measureText, measurePrefix);
}
} else if (measuring) {
this.stopMeasure();
}
},
onInputFocus: function onInputFocus(event) {
this.onFocus(event);
},
onInputBlur: function onInputBlur(event) {
this.onBlur(event);
},
onDropdownFocus: function onDropdownFocus() {
this.onFocus();
},
onDropdownBlur: function onDropdownBlur() {
this.onBlur();
},
onFocus: function onFocus(event) {
window.clearTimeout(this.focusId);
var isFocus = this.$data.isFocus;
if (!isFocus && event) {
this.$emit('focus', event);
}
this.setState({ isFocus: true });
},
onBlur: function onBlur(event) {
var _this2 = this;
this.focusId = window.setTimeout(function () {
_this2.setState({ isFocus: false });
_this2.stopMeasure();
_this2.$emit('blur', event);
}, 0);
},
selectOption: function selectOption(option) {
var _this3 = this;
var _$data3 = this.$data,
value = _$data3._value,
measureLocation = _$data3.measureLocation,
measurePrefix = _$data3.measurePrefix;
var split = this.$props.split;
var _option$value = option.value,
mentionValue = _option$value === undefined ? '' : _option$value;
var _replaceWithMeasure = replaceWithMeasure(value, {
measureLocation: measureLocation,
targetText: mentionValue,
prefix: measurePrefix,
selectionStart: this.$refs.textarea.selectionStart,
split: split
}),
text = _replaceWithMeasure.text,
selectionLocation = _replaceWithMeasure.selectionLocation;
this.triggerChange(text);
this.stopMeasure(function () {
// We need restore the selection position
setInputSelection(_this3.$refs.textarea, selectionLocation);
});
this.$emit('select', option, measurePrefix);
},
setActiveIndex: function setActiveIndex(activeIndex) {
this.setState({
activeIndex: activeIndex
});
},
getOptions: function getOptions(measureText) {
var targetMeasureText = measureText || this.$data.measureText || '';
var _$props3 = this.$props,
filterOption = _$props3.filterOption,
_$props3$children = _$props3.children,
children = _$props3$children === undefined ? [] : _$props3$children;
var list = (Array.isArray(children) ? children : [children]).map(function (item) {
var children = getSlots(item)['default'];
return _extends({}, getOptionProps(item), { children: children });
}).filter(function (option) {
/** Return all result if `filterOption` is false. */
if (filterOption === false) {
return true;
}
return filterOption(targetMeasureText, option);
});
return list;
},
startMeasure: function startMeasure(measureText, measurePrefix, measureLocation) {
this.setState({
measuring: true,
measureText: measureText,
measurePrefix: measurePrefix,
measureLocation: measureLocation,
activeIndex: 0
});
},
stopMeasure: function stopMeasure(callback) {
this.setState({
measuring: false,
measureLocation: 0,
measureText: null
}, callback);
},
focus: function focus() {
this.$refs.textarea.focus();
},
blur: function blur() {
this.$refs.textarea.blur();
}
},
render: function render() {
var h = arguments[0];
var _$data4 = this.$data,
value = _$data4._value,
measureLocation = _$data4.measureLocation,
measurePrefix = _$data4.measurePrefix,
measuring = _$data4.measuring;
var _getOptionProps = getOptionProps(this),
prefixCls = _getOptionProps.prefixCls,
placement = _getOptionProps.placement,
transitionName = _getOptionProps.transitionName,
autoFocus = _getOptionProps.autoFocus,
notFoundContent = _getOptionProps.notFoundContent,
getPopupContainer = _getOptionProps.getPopupContainer,
restProps = _objectWithoutProperties(_getOptionProps, ['prefixCls', 'placement', 'transitionName', 'autoFocus', 'notFoundContent', 'getPopupContainer']);
var inputProps = omit(restProps, ['value', 'defaultValue', 'prefix', 'split', 'children', 'validateSearch', 'filterOption']);
var options = measuring ? this.getOptions() : [];
return h(
'div',
{ 'class': prefixCls },
[h('textarea', _mergeJSXProps([{
ref: 'textarea'
}, {
directives: [{ name: 'ant-input' }],
attrs: _extends({}, inputProps, this.$attrs),
domProps: {
value: value
},
on: _extends({}, getListeners(this), {
select: noop,
change: noop,
input: this.onChange,
keydown: this.onKeyDown,
keyup: this.onKeyUp,
blur: this.onInputBlur
})
}])), measuring && h(
'div',
{ ref: 'measure', 'class': prefixCls + '-measure' },
[value.slice(0, measureLocation), h(
KeywordTrigger,
{
attrs: {
prefixCls: prefixCls,
transitionName: transitionName,
placement: placement,
options: options,
visible: true,
getPopupContainer: getPopupContainer
}
},
[h('span', [measurePrefix])]
), value.slice(measureLocation + measurePrefix.length)]
)]
);
}
};
export default Mentions;