Last active
February 10, 2022 16:50
-
-
Save padcom/872cddfe13d19b56295769944eed226b to your computer and use it in GitHub Desktop.
Example completely custom Vue 3 component with contenteditable and v-model support
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Document</title> | |
<style> | |
* { | |
box-sizing: border-box; | |
} | |
.example { | |
background-color: lightgray; | |
margin-bottom: 10px; | |
} | |
.example[contenteditable] { | |
min-height: 100px; | |
padding: 2px; | |
border: solid 2px transparent; | |
} | |
.example[contenteditable]:focus { | |
border: dashed 2px black; | |
outline: none; | |
} | |
</style> | |
<script type="importmap"> | |
{ | |
"imports": { | |
"vue": "https://unpkg.com/[email protected]/dist/vue.esm-browser.prod.js" | |
} | |
} | |
</script> | |
</head> | |
<body> | |
<div id="app"> | |
<example-abc v-model="message" v-focus></example-abc> | |
{{ message }} | |
</div> | |
<template id="example"> | |
<div class="example" tabindex="0" contenteditable="plaintext-only" @input="$emit('update:modelValue', $event.target.innerText)"> | |
{{ value }} | |
</div> | |
</template> | |
<script type="module"> | |
import { createApp, defineComponent } from 'vue' | |
const Example = defineComponent({ | |
template: '#example', | |
props: [ 'modelValue' ], | |
events: [ 'update:modelValue' ], | |
data() { | |
// Working with the initial value because when the div's content changes | |
// the entire value is sent back over to the parent and then re-rendered | |
// in this component. This messes up a lot of things like new lines, | |
// copy/pasting and others. | |
return { | |
value: this.modelValue | |
} | |
}, | |
watch: { | |
modelValue(value) { | |
this.value = value | |
}, | |
}, | |
}) | |
const app = createApp({ | |
data() { | |
return { | |
message: 'Hello, world!' | |
} | |
}, | |
}) | |
app.directive('focus', { | |
mounted(el) { | |
el.focus() | |
} | |
}) | |
app.component('example-abc', Example) | |
app.mount('#app') | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment