Select Control Based on Element UI to Realize Single Line Display in Multi-Choice Box-Moving Left and Right

Keywords: TypeScript Mobile

The main function of the control is to display multiple tags in a single line, and to move tags in components by using left and right keys.

Code directly:

<el-select
			v-model="tags"
			ref="tagsSelect"
			multiple
			filterable
			remote
			clearable
			popper-class="tag-input-options"
			:remote-method="searchTags"
			:loading="isSearching"
			class="tag-input"
			@keyup.native.left="navLeft"
			@keyup.native.right="navRight">
			<el-option
				v-for="item in tagOptions"
				:key="item.id"
				:label="item.full_path.map(f => f.name).join('  >  ')"
				:value="item.id">
				<span class="tag-option" v-html="highlightTagOption(item.name)"></span>
			</el-option>
  		</el-select>

The following is the processing logic, mainly the implementation of navLeft and navRight methods (typescript):

The main idea is to change left and control the displacement of tag in select.

const TAGS_SELECTOR = '.tag-input .el-select__tags';
const TAG_SELECTOR = '.tag-input .el-select__tags .el-tag';

private navLeft() {

		let $hitTagDoms = $(TAG_SELECTOR + '.is-hit');
		if (!$hitTagDoms.length) {
			$(TAG_SELECTOR).last().addClass('is-hit');
			return;
		} else if ($hitTagDoms.is($(TAG_SELECTOR).first())) {
			$(TAG_SELECTOR).removeClass('is-hit');
			$(TAG_SELECTOR).last().addClass('is-hit');
			$(TAGS_SELECTOR).css('left', '11px');
			return;
		}

		let $lastTagDom = $hitTagDoms.hasClass('is-hit') ? $hitTagDoms.prev() : $hitTagDoms;
		$(TAG_SELECTOR).removeClass('is-hit');
		$lastTagDom.addClass('is-hit');

		let firstTagRect = $(TAG_SELECTOR).get(0).getBoundingClientRect() as DOMRect;
		let containerTagRect = $('.tag-input').get(0).getBoundingClientRect() as DOMRect;
		
		let left = Number.parseFloat($(TAGS_SELECTOR).css('left'));
		left = Number.isNaN(left) ? 0 : left;
		let totalLeft = left + ($lastTagDom.innerWidth() as number);

		if (totalLeft >= containerTagRect.x - firstTagRect.x + 11) { // 11 is padding left of tags
			totalLeft = left + containerTagRect.x - firstTagRect.x + 11;
		}
		
		$(TAGS_SELECTOR).css('left', totalLeft + 'px');
	}
private navRight() {

		let $lastHitTagDoms = $(TAG_SELECTOR + '.is-hit');
		if (!$lastHitTagDoms.length || $lastHitTagDoms.is($(TAG_SELECTOR).last())) { return; }

		$(TAG_SELECTOR).removeClass('is-hit');
		let $hitTagDoms = $lastHitTagDoms.next();
		$hitTagDoms.addClass('is-hit');

		let hitTagRect = $hitTagDoms.get(0).getBoundingClientRect() as DOMRect;
		let containerTagRect = $('.tag-input').get(0).getBoundingClientRect() as DOMRect;

		if (hitTagRect.right + 56 > containerTagRect.right) { // 56 is width of search button
			let left = Number.parseFloat($(TAGS_SELECTOR).css('left'));
			left = Number.isNaN(left) ? 0 : left;
			let totalLeft = left - ($hitTagDoms.innerWidth() as number);
			if ($hitTagDoms.is($(TAG_SELECTOR).last())) { // assure input showing
				totalLeft = 0;
			}
			$(TAGS_SELECTOR).css('left', totalLeft + 'px');
		}
	}

In order not to allow select controls to switch multiple lines, you need to override the relevant css:

.tag-input {
		width: 645px;
		overflow-x:hidden;

		.el-select__tags {
			flex-wrap: nowrap;
			justify-content: flex-end;
			transition: left .5s;
        }
}

There are still several problems in the above way of implementation:

1) The left and right keys affect the position of the input cursor in select

2) When the drop-down box is displayed, deleting the tag will cause the position deviation of the drop-down box.

3) To slide the tag to the beginning, deleting the tag from the beginning will create a blank because the left value has not been adjusted.

It can provide solutions:

1) The mobile tag can be controlled without the left and right keys, and replaced with other keys.

2) When deleting, hide the drop-down box manually and display it when entering the search.

3) When deleting, adjust the left value

private renderingTag() {
		let $firstTagDom = $(TAG_SELECTOR + ':first-child');
		if ($firstTagDom.length) {
			let firstTagRect = $firstTagDom.get(0).getBoundingClientRect() as DOMRect;
			let containerTagRect = $('.tag-input').get(0).getBoundingClientRect() as DOMRect;

			if (firstTagRect.left > containerTagRect.left + 11) { // 11 is margin left
				let left = Number.parseFloat($(TAGS_SELECTOR).css('left'));
				left = Number.isNaN(left) ? 0 : left;
				left -= (firstTagRect.left - containerTagRect.left - 11);
				$(TAGS_SELECTOR).css('left', left + 'px');
			}
		}
	}

 

Look at the picture below.

Posted by simwiz on Sun, 06 Oct 2019 11:37:46 -0700