TextEditor
The <TextEditor>
is a WYSIWYG (What You See Is What You Get) editor - An editor that allows users to create and edit rich text content in a form that resembles its appearance when displayed as a finished product.
It leverages Quill.js as the underlying editor, which is a powerful, extensible, and a customizable rich text editor.
Basic Usage
Diplayed content
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
const modelValue = ref('');
</script>
<template>
<TextEditor
id="text-editor-basic"
v-model="modelValue"
label="Description"
placeholder="Insert a product description"
/>
<div class="tw-mt-2">
<p class="">Diplayed content</p>
<pre class="tw-text-wrap">
{{ modelValue }}
</pre>
</div>
</template>
Placeholder
The placeholder is a text that appears in the editor when it is empty. Quill.js adds the placeholder as a content property of a ::before
pseudo-element, but you can change its text with the placeholder
prop.
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
const modelValue = ref('');
</script>
<template>
<TextEditor id="text-editor-placeholder" v-model="modelValue" placeholder="Product description" />
</template>
Field props
The <TextEditor>
component uses the Field component internally, so you can pass most of the field properties to it; the Field props available for use are listed below.
Label
Adds a label above the text editor.
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
const modelValue = ref('');
</script>
<template>
<TextEditor id="text-editor-label" v-model="modelValue" label="Product description" />
</template>
Read only
Styles the field as read-only. This is useful when displaying the editor rich text because it will have all the same styles and spacing of a regular field.
INFO
Quill.js sets the editor's contenteditable
to false when isReadOnly
is true to prevent the user from editing the content.
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
const modelValue = ref(`
<p><strong>Quality Glass, For Every Stoner</strong></p>
<p></br></p>
<p>Thick Ass Glass (TAG) encompasses the meaning of high quality affordable borosilicate. <strong>These pieces can take a beating while delivering smooth hits from even the biggest clouds</strong>.</p>
<p></br></p>
<p>The TAG logo is sand-blasted on the neck of the bong.</p>
<p></br></p>
<ul>
<li>10" Bent Neck</li>
<li>Matrix Diffuser</li>
<li>65x5MM</li>
<li>14mm Female Down Stem</li>
</ul>
`);
</script>
<template>
<TextEditor id="text-editor-read-only" v-model="modelValue" label="Product description" is-read-only />
</template>
Add bottom space
Adds spacing under the field that is consistent whether hint/error text is displayed.
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
import Button from '../../../src/components/Button/Button.vue';
import Input from '../../../src/components/Input/Input.vue';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
const modelValue = ref('');
const bottomSpace = ref(true);
</script>
<template>
<Button class="tw-mb-6" @click="bottomSpace = !bottomSpace">Toggle bottom space</Button>
<TextEditor
id="text-editor-bottom-space"
v-model="modelValue"
label="Description"
placeholder="Insert a product description"
class="tw-mb-2"
:add-bottom-space="bottomSpace"
/>
<Input label="Product Category" :add-bottom-space="bottomSpace" />
</template>
Required
The required
prop adds a red asterisk next to the label.
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
const modelValue = ref('');
</script>
<template>
<TextEditor id="text-editor-hint-text" v-model="modelValue" label="Product description" is-required />
</template>
Optional
The show-optional-in-label
prop displays (optional)
to the right of the label text.
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
const modelValue = ref('');
</script>
<template>
<TextEditor id="text-editor-hint-text" v-model="modelValue" label="Product description" show-optional-in-label />
</template>
Hint text
Displays the provided text below the TextEditor. See more about this property in the hint-text section of the Field component.
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
const modelValue = ref('');
</script>
<template>
<TextEditor id="text-editor-hint-text" v-model="modelValue" hint-text="The product description" />
</template>
Error Text
Displays the provided text in red below the input. See more about this property in the error-text section of the Field component.
INFO
The hint-text
will be replaced by the error-text
if both are provided.
<script setup lang="ts">
import { defineAsyncComponent, Ref, ref } from 'vue';
import Button from '../../../src/components/Button/Button.vue';
import useValidation, { isDefined } from '../../../src/composables/useValidation/useValidation';
import { ValidationRules } from '../../../src/composables/useValidation/useValidation.types';
import { t } from '../../../src/locale';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
interface FormState {
description?: string;
}
const formState: Ref<FormState> = ref(getInitialFormState());
function getInitialFormState() {
return {
description: undefined,
};
}
const validationRules: ValidationRules<FormState> = {
description: [
{
name: 'required',
validator(value) {
return isDefined(value) && value !== '<p><br></p>';
},
message: t('ll.validation.required'),
},
],
};
const validation = useValidation({ rules: validationRules, values: formState });
async function onSubmit() {
await validation.validate();
if (validation.hasErrors) {
return;
}
alert(`Form submitted\n ${JSON.stringify(formState.value, null, 2)}`);
}
</script>
<template>
<form @submit.prevent>
<TextEditor
id="text-editor-validation"
v-model="formState.description"
:error-text="validation.getError('description')"
label="Product template"
hint-text="The product description"
add-bottom-space
@blur="validation.touch('description')"
/>
<Button class="tw-mt-2" type="button" @click="onSubmit">Submit</Button>
</form>
</template>
Disabled
The isDisabled
prop disables the editor and prevents the user from interacting with it.
INFO
Quill.js sets the editor's contenteditable
to false when isDisabled
is true to prevent the user from editing the content.
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
const modelValue = ref('');
</script>
<template>
<TextEditor
id="text-editor-read-only"
v-model="modelValue"
label="Product description"
placeholder="Insert a product description"
is-disabled
/>
</template>
Controls
The controls
prop can be a set of format bar buttons which can be either a list of single controls or a list of a grouped control arrays.
See the Quill.js documentation for more information about the available controls.
INFO
For a design reasons, the <TextEditor>
component only supports this small list of controls: bold
, underline
, italic
, list
(bullet
, ordered
), link
, divider
.
Simple controls
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
import { TextEditorControls } from '../../../src/components/TextEditor/TextEditor.vue';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
const modelValue = ref('');
const controls: TextEditorControls = ['bold', 'italic'];
</script>
<template>
<TextEditor id="text-editor-controls" v-model="modelValue" :controls="controls" />
</template>
Grouped controls
The controls
props can receive a matrix indicating that each matrix row is a grouped set of controls like:
[
['bold', 'italic', 'underline'],
['link'],
['divider']
]
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
import { TextEditorControls } from '../../../src/components/TextEditor/TextEditor.vue';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
const modelValue = ref('');
const controls: TextEditorControls = [
['bold', 'italic'],
[{ list: 'bullet' }, { list: 'ordered' }],
];
</script>
<template>
<TextEditor id="text-editor-grouped-controls" v-model="modelValue" :controls="controls" />
</template>
Control handlers
The handlers
prop can receive an object mapper with custom control handlers which will dictate each control behavior upon click.
<script setup lang="ts">
import { defineAsyncComponent, ref } from 'vue';
import { TextEditorControlHandlerMap } from '../../../src/components/TextEditor/TextEditor.vue';
// Since Quill uses the browser api, defineAsyncComponent can lazy load the Text Editor in a non SSR environment
const TextEditor = defineAsyncComponent(() => import('../../../src/components/TextEditor/TextEditor.vue'));
const modelValue = ref('');
const handlers: TextEditorControlHandlerMap = {
bold: () => alert('Bold clicked'),
italic: () => alert('Italic clicked'),
};
</script>
<template>
<TextEditor id="text-editor-control-handlers" v-model="modelValue" :handlers="handlers" />
</template>
API
See the documentation below for a complete reference to all the props and classes available to the components mentioned here.