Documentation Index
Fetch the complete documentation index at: https://mintlify.com/euclidesseg/euclides-workspace/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Euclides Rich Editor supports both ordered and unordered lists through the toggleList method. List functionality is provided by ProseMirror’s prosemirror-schema-list package and integrated into the custom schema.
Using toggleList
The toggleList method creates or converts content to a list:
toggleList(type: list, view: EditorView): boolean {
return CommandsMethods.switchList(EuclidesEditorSchema.nodes[type])(view.state, view.dispatch);
}
Usage in your component:
import { EditorCommandsService } from 'euclides-rich-editor';
import { list } from 'euclides-rich-editor/core/types';
toggleList(type: list) {
if (this.editorCommandsService.toggleList(type, this.view)) {
this.view.focus();
}
}
Example with buttons:
<button (click)="toggleList('bullet_list')">• Bullet List</button>
<button (click)="toggleList('ordered_list')">1. Ordered List</button>
List Types
The editor defines a list type for supported list formats:
export type list = 'ordered_list' | 'bullet_list' | 'task_list'
Source: ~/workspace/source/projects/euclides-rich-editor/src/lib/core/types/list.type.ts:1
Currently, the editor implements ordered_list and bullet_list. The task_list type is defined but not yet fully implemented.
Ordered Lists
Numbered lists for sequential content:
toggleList('ordered_list');
Generates HTML:
<ol>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ol>
Unordered Lists
Bullet lists for non-sequential content:
toggleList('bullet_list');
Generates HTML:
<ul>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ul>
How switchList Works
The switchList method provides intelligent list toggling:
static switchList(listType: NodeType): Command {
return (state, dispatch) => {
const { schema, selection } = state;
const { $from, from, to } = selection;
let tr = state.tr;
// Convert heading to paragraph first
if ($from.parent.type === schema.nodes['heading']) {
tr = tr.setBlockType(from, to, schema.nodes['paragraph']);
}
const resolved = tr.doc.resolve(tr.selection.from);
for (let d = resolved.depth; d > 0; d--) {
const node = resolved.node(d);
if (
node.type === schema.nodes['bullet_list'] ||
node.type === schema.nodes['ordered_list']
) {
// If already this list type, lift it out
if (node.type === listType) {
return liftListItem(schema.nodes['list_item'])(state, dispatch);
}
// Convert between list types
if (dispatch) {
dispatch(tr.setNodeMarkup(resolved.before(d), listType));
}
return true;
}
}
// Not in a list, wrap in one
return wrapInList(listType)(state, dispatch);
};
}
Source: ~/workspace/source/projects/euclides-rich-editor/src/lib/engine/commanmethods/command.methods.ts:60-92
Behavior Patterns
Heading to List
If the selection is in a heading, it’s first converted to a paragraph, then wrapped in a list:if ($from.parent.type === schema.nodes['heading']) {
tr = tr.setBlockType(from, to, schema.nodes['paragraph']);
}
Toggle Same List Type
If already in the same list type, lift the item out of the list:if (node.type === listType) {
return liftListItem(schema.nodes['list_item'])(state, dispatch);
}
Convert Between Lists
If in a different list type, convert it:dispatch(tr.setNodeMarkup(resolved.before(d), listType));
Wrap in New List
If not in a list, wrap the selection:return wrapInList(listType)(state, dispatch);
Schema Integration
Lists are added to the schema using addListNodes from prosemirror-schema-list:
import { addListNodes } from 'prosemirror-schema-list';
const nodes = basicSchema.spec.nodes.update("paragraph", paragraph);
export const EuclidesEditorSchema = new Schema({
nodes: addListNodes(nodes, "paragraph block*", "block"),
marks: basicSchema.spec.marks.addToEnd("strike", strike),
});
Source: ~/workspace/source/projects/euclides-rich-editor/src/lib/engine/schema/euclides-schema.ts:47-52
Understanding addListNodes
The addListNodes function adds three node types to the schema:
bullet_list - The unordered list container (<ul>)
ordered_list - The ordered list container (<ol>)
list_item - Individual list items (<li>)
Parameters:
addListNodes(
nodes, // Existing nodes OrderedMap
"paragraph block*", // Content expression for list items
"block" // Group name for list nodes
)
"paragraph block*" - List items can contain a paragraph followed by any number of blocks
"block" - Lists are block-level elements
List and Code Block Integration
The toggleCodeBlock method has special handling for lists. When converting a list to a code block, it extracts all the text:
if (
node.type === schema.nodes['bullet_list'] ||
node.type === schema.nodes['ordered_list']
) {
// Extract text from all list items
let text = "";
node.forEach((listItem: any) => {
const para = listItem.firstChild;
if (para) {
text += para.textContent + "\n";
}
});
text = text.trimEnd();
const codeBlock = schema.nodes['code_block'].create(
null,
schema.text(text)
);
if (dispatch) {
dispatch(
tr.replaceWith(
$from.before(d),
$from.after(d),
codeBlock
)
);
}
return true;
}
Source: ~/workspace/source/projects/euclides-rich-editor/src/lib/engine/commanmethods/command.methods.ts:15-50
This allows seamless conversion from:
• Item one
• Item two
• Item three
To:
Item one
Item two
Item three
Document Structure
Lists create a nested document structure:
{
"type": "bullet_list",
"content": [
{
"type": "list_item",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "First item" }
]
}
]
},
{
"type": "list_item",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "Second item" }
]
}
]
}
]
}
Each list item contains a paragraph by default, which allows for nested blocks like additional paragraphs or even nested lists within a single item.
Best Practices
Use type-safe list types
Import and use the list type for type safety:import { list } from 'euclides-rich-editor/core/types';
toggleList(type: list) {
this.editorCommandsService.toggleList(type, this.view);
}
Handle toggle behavior
Clicking the same list button twice will remove the list:// First click: wrap in bullet list
toggleList('bullet_list');
// Second click on same button: unwrap list
toggleList('bullet_list');
Understand list conversion
Switching between list types preserves items:// Start with bullet list
toggleList('bullet_list');
// Convert to ordered list (keeps items)
toggleList('ordered_list');
Advanced: Nested Lists
To create nested lists, users can:
- Create a list item
- Manually adjust structure through the ProseMirror API
Tab key indentation for nested lists requires additional input rules configuration. You can implement this by adding custom input rules to the editor plugins. See the Custom Schema guide for information on extending the editor.
Next Steps