HTML Rich Text Editor
Source code
<template> <p> Double-click to edit. Valid changes save automatically; Escape discards the draft. </p> <RevoGrid :source="source" :columns="columns" :plugins="plugins" :theme="isDark ? 'darkMaterial' : 'material'" style="height: 340px" /></template><script setup lang="ts">import { ref } from 'vue';import { currentThemeVue } from '../composables/useRandomData';import RevoGrid from '@revolist/vue3-datagrid';import { editorColumns, editorRows } from './editor-data';const { isDark } = currentThemeVue();const source = ref(editorRows());const columns = editorColumns();const plugins = [];</script>import { currentTheme } from '../composables/useRandomData';import { defineCustomElements } from '@revolist/revogrid/loader';import { editorColumns, editorRows } from './editor-data';defineCustomElements();export function load(parentSelector: string, rows = editorRows()) { const grid = document.createElement('revo-grid'); grid.theme = currentTheme().isDark() ? 'darkMaterial' : 'material'; grid.style.height = '340px'; grid.columns = editorColumns(); document.querySelector(parentSelector)?.append(grid); grid.source = rows; return () => grid.remove();}import { currentTheme } from '../composables/useRandomData';import { useMemo } from 'react';import { RevoGrid } from '@revolist/react-datagrid';import { editorColumns, editorRows } from './editor-data';export default function EditorRichText({ rows }: { rows?: any[] }) { const columns = useMemo(editorColumns, []); const fallback = useMemo(editorRows, []); const plugins = useMemo(() => [], []); return ( <> <p> Double-click to edit. Valid changes save automatically; Escape discards the draft. </p> <RevoGrid source={rows?.length ? rows : fallback} columns={columns} plugins={plugins} theme={currentTheme().isDark() ? 'darkMaterial' : 'material'} style={{ height: '340px' }} /> </> );}import { currentTheme } from '../composables/useRandomData';import { Component, ViewEncapsulation } from '@angular/core';import { RevoGrid } from '@revolist/angular-datagrid';import { editorColumns, editorRows } from './editor-data';@Component({ selector: 'editor-rich-text-grid', standalone: true, imports: [RevoGrid], encapsulation: ViewEncapsulation.None, template: `<p> Double-click to edit. Valid changes save automatically; Escape discards the draft. </p> <revo-grid [source]="source" [columns]="columns" [plugins]="plugins" [theme]="theme" style="height:340px" ></revo-grid>`,})export class EditorRichTextGridComponent { theme = currentTheme().isDark() ? 'darkMaterial' : 'material'; source = editorRows(); columns = editorColumns(); plugins = [];}import type { ColumnRegular } from '@revolist/revogrid';import { RichTextEditor, htmlRenderer,} from '@revolist/revogrid-pro/editors/rich-text';import '@revolist/revogrid-pro/editors/rich-text.css';export const editorRows = () => [ { name: 'Planning', value: '<p>Review <strong>scope</strong> and <a href="https://rv-grid.com">requirements</a>.</p>', },];export const editorColumns = (): ColumnRegular[] => [ { prop: 'name', name: 'Task', size: 210, readonly: true }, { prop: 'value', name: 'HTML notes · Visual / source', size: 620, editor: RichTextEditor, cellTemplate: htmlRenderer, richText: { maxLength: 50000 }, },];Import RichTextEditor and htmlRenderer from the optional @revolist/revogrid-pro/editors/rich-text entry, and import @revolist/revogrid-pro/editors/rich-text.css. Set editor: RichTextEditor and cellTemplate: htmlRenderer on a column, or use ColumnRichText as its column type.
The editor stores sanitized HTML strings. It uses RevoGrid’s shared UI and native browser selection APIs, with no third-party editor or Markdown dependencies. Visual and HTML source modes share one draft. The compact toolbar supports bold, italic, strike, inline code, H1/H2, bullet and numbered lists, quotes, code blocks and links.
Enter inserts a new paragraph (a newline in a code block); Shift+Enter inserts a line break. Ctrl/Cmd+B and Ctrl/Cmd+I toggle inline formatting, Ctrl/Cmd+Z undoes local edits, and Ctrl/Cmd+Shift+Z or Ctrl+Y redoes them. Ctrl/Cmd+Enter applies one grid edit. Paste inserts plain text. Tab and Shift+Tab indent and outdent list items.
By default, clicking outside applies a changed valid draft. Escape cancels it. Set richText.autoApply: false to require explicit Apply and show Apply/Cancel controls. richText.showActions controls their visibility independently. Clear remains available for optional values. richText.required checks visible text; richText.maxLength limits the serialized sanitized HTML string.
Source input is parsed in an inert template and rebuilt using an allowlist before it reaches Visual mode, a saved value, or the cell renderer. Supported tags are paragraphs, H1–H6, strong, em, s, code, pre, blockquote, ul, ol, li, br and links. Basic formatting aliases such as b and i are normalized. Event handlers, styles, images, embedded content, scripts and foreign markup are removed. Links allow HTTP, HTTPS, mailto and relative URLs. htmlRenderer creates native Stencil nodes from that same sanitized model; it never injects raw HTML. Applications must still validate untrusted values on their server.
Use richText.text to translate mode names, popup and toolbar labels, link input and validation messages. Formatting keys include bold, italic, strike, code, heading1, heading2, bullets, numberedList, quote and codeBlock; formatting, inlineFormatting, headings, structure and links label toolbar groups. Partial richText.actions translates Apply, Cancel and Clear.