Basic introduction
Now there are a lot of component library based on Vue, but look at the message has not been seen a lot of comments of components, this to want to do some articles information display class projects can seem difficult, because there are too many page need this function, do we need to repeat to write (copy and paste the code? For the modular system is now gradually perfect front-end engineering projects, one-time encapsulation of a common function of the component is very necessary, and now we go to encapsulate such a component!
Required Technology (Vue)
Since the packaged components are based on Vue, this requires us to master some knowledge of Vue (Vue is recommended to go to the official documentation for related knowledge :grinning:), Students with basic Vue skills can deepen their understanding of Vue componentized programming by encapsulating such a fully-functional component. Now let’s implement the encapsulation of this component
Essential text input box
We know that native HTML elements only form elements can enter text, but these elements are rendered differently by default in different browsers and do not support formatted text, so we need to use Div elements to simulate an input box with the ability to format text.
How to get divs to enter text is a new HTML5 property, “contenteditable”. The attribute value is a Boolean type, adding this attribute tag element and set its attributes value is true (the default is not set the browser parses to true), and now this element is a can edit the content of elements, the user can like use form elements to use it, now let us use code to demonstrate it
<! Only the main code is shown here
<style>
.inputBox{
border:1px solid;
height:200px;
width:400px;
}
</style>
<div class="inputBox" contenteditable="true">I'm an editable element</div>
Copy the code
This is what the page actually looks like
So here we have a text field, and then you can add some other styles to it to make it better, which I won’t show you here.
Encapsulate input box components
For better code logic decoupling and later component maintainability, we separate parts of the editor input box into a component for encapsulation. With the previous knowledge, packaging such a component is very easy to use, specific is how to achieve we directly look at the code!
<template>
<div class>
<div type="text" class="input-box-wrapper">
<div
:class="['content',{focused},type]"
ref="richText"
v-on="listeners"
v-bind="$attrs"
:contenteditable="contenteditable"
></div>
<div class="append-wrapper">
<slot name="append"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'input-box'.data() {
return {
contenteditable: true}},computed: {
listeners() {
return Object.assign(
{},
this.$listeners,
{
input: function(e) {
const inputContent =
this.contentType === 'plain'
? e.target.textContent
: e.target.innerHTML
this.$emit('input', inputContent)
}.bind(this)})}},props: {
focused: {
type: Boolean.default: false
},
contentType: {
type: String.default: 'plain'.validator(value) {
return ['plain'.'rich'].includes(value)
}
},
type: {
type: String.default: 'text'.validator(value) {
return ['text'.'textarea'].includes(value)
}
},
rows: Number
},
methods: {
focus() {
this.$refs.richText.focus()
}
}
}
</script>
Copy the code
The actual component looks like this
At this point, we have a text field component that is simulated with the Div element.
Note: The complete code for the input-box component comes from the open source project comment-message-Editor
Rich emoji picker component (: Smile:)
Mixing text and emoticons when we leave a comment on an article or a review can make our language more concise and expressive. So why encapsulate the symbol selector component? The answer is: in an increasingly complex front-end engineering environment, we need to remove all future functional modules that can be reused or extended to be maintained, so that we can maintain and commit them in the future, whether we change the code or add new functionality. Here incidentally also the component of the source code posted out total we usually do the same type of function can refer to.
The emoji-picker.vue code comes from the open source project comment-Editor
<template>
<div
@keyup.esc="hidePicker"
ref="container"
class="emoji-wrapper"
hidefocus="true"
v-on="handleMouse()"
>
<span class="emoji-button" @click.stop="togglePickerVisibility">
<img
:class="{inactive:! pickerVisible}"
class="button-icon"
src=".. /emoji/icon.svg"
width="20"
height="20"
alt
/>
<span v-if="buttonTextVisible" class="button-text">expression</span>
</span>
<ul :class="['emoji-picker',pickerPosition]" v-if="pickerVisible">
<li v-for="(url,key) in files" :key="key" class="emoji-picker-item">
<img class="emoji-icon" @click="handlerSelect" width="20" height="20" :src="url" alt />
</li>
</ul>
</div>
</template>
<script type="text/javascript">
const path = require('path')
const requireEmoji = require.context('.. /emoji')
let files = requireEmoji.keys()
export default {
data() {
return {
pickerVisible: false.files: files.map(url= > require(`.. /emoji/${url.slice(2)}`}})),props: {
buttonTextVisible: {
type: Boolean.default: true
},
triggerPick: {
tyep: String.default: 'hover'.validator(value) {
return ['hover'.'click'].includes(value)
}
},
pickerPosition: {
type: String.default: 'right'.validator(value) {
return ['left'.'middle'.'right'].includes(value)
}
}
},
watch: {
pickerVisible(newValue) {
newValue ? this.$emit('activated') : this.$emit('inactivated')}},mounted() {
const docHandleClick = (this.docHandleClick = e= > {
if (!this.$refs.container.contains(e.target)) {
this.hidePicker()
}
})
const handleKeyup = (this.handleKeyup = e= > {
if (e.key === 'Escape') {
this.hidePicker()
}
})
document.addEventListener('click', docHandleClick)
document.addEventListener('keyup', handleKeyup)
},
destroyed() {
document.removeEventListener('click'.this.docHandleClick)
document.removeEventListener('click'.this.handleKeyup)
},
methods: {
handlerSelect(e) {
this.$emit('selected', e)
},
hidePicker() {
this.pickerVisible = false
},
togglePickerVisibility() {
if (this.triggerPick === 'click') {
this.pickerVisible = !this.pickerVisible
}
},
handleMouse() {
const mouseenter = function() {
this.pickerVisible = true
}.bind(this)
const mouseleave = function() {
this.pickerVisible = false
}.bind(this)
if (this.triggerPick === 'hover') {
return {
mouseenter,
mouseleave
}
} else {
return{}}}}}</script>
Copy the code
Assemble the editor entry component
We have the input box component and the emoticon picker component, and we just need to combine them in a certain way, and our comment editor component is done. So without further ado let’s look at the code
The source code for main.vue comes from the open source project comment-message-Editor
<template>
<div class="comment-editor" ref="container">
<div class="input-wrapper" :class="{inline}">
<input-box
ref="inputBox"
:type="inline? 'text':'textarea'"
content-type="rich"
:rows="2"
@focus="onInputFocus"
@blur="onInputBlur"
@keyup.enter.ctrl.exact.native="handlerSubmit"
v-model="inputContent"
:placeholder="'placeholder'"
:focused="showInlineButton"
class="input-box"
>
<div v-if="inline" :class="['input-append',{hasbg:!showInlineButton}]" slot="append">
<emoji-picker
ref="emojiPicker"
trigger-pick="click"
@activated="inputBoxFocused=true"
@selected="handlerEmojiSelected"
picker-position="left"
:button-text-visible="false"
></emoji-picker>
</div>
</input-box>
<transition name="button" >
<div
@click="handlerSubmit"
class="submit-button inline"
:disabled=! "" inputContent"
ref="button"
v-show="showInlineButton && inline"
>{{buttonText}}</div>
</transition>
</div>
<div class="footer-action" v-if=! "" inline">
<emoji-picker
trigger-pick="click"
@activated="$refs.inputBox.focus()"
@selected="handlerEmojiSelected"
></emoji-picker>
<span class="submit-tiptext">Ctrl + Enter</span>
<div @click="handlerSubmit" class="submit-button" :disabled=! "" inputContent">{{buttonText}}</div>
</div>
</div>
</template>
<script>
import InputBox from './components/input-box'
import EmojiPicker from './components/emoji-picker'
export default {
name: 'comment-editor'.components: { InputBox, EmojiPicker },
data() {
return {
active: false.inputContent: ' '.inputBoxFocused: false}},props: {
buttonText: {
type: String.default: 'submit'
},
inline: {
type: Boolean.default: false}},computed: {
showInlineButton() {
return!!!!! (this.inputBoxFocused || this.inputContent)
}
},
destroyed() {
document.removeEventListener('click'.this.hideButton)
},
mounted() {
document.addEventListener('click'.this.hideButton)
},
methods: {
focus(){
this.$refs.inputBox.focus()
},
hideButton(e) {
if (this.$refs.container.contains(e.target)) {
return
}
if (!this.$refs.container.contains(e.target)) {
this.inputBoxFocused = false}},onInputFocus(e) {
this.inputBoxFocused = true
},
onInputBlur(e) {
if (this.$refs.container.contains(e.target)) {
return
}
this.inputBoxFocused = false
},
handlerSubmit(e) {
if (e.target.hasAttribute('disabled')) {
return
}
this.$emit('submit'.this.inputContent)
},
handlerEmojiSelected(e) {
this.$refs.inputBox.focus()
const clonedNode = e.target.cloneNode(true)
clonedNode.style.verticalAlign = 'text-top'
document.execCommand('insertHTML'.false, clonedNode.outerHTML)
}
}
}
</script>
Copy the code
Component preview
The warehouse address of this project is attached at the end of the article
Note: this article belongs to the author’s original, reprint please indicate the source, thank you! Author: Kong Lingwen