NCC-1701-Developer
NCC-1701-Developer

Reputation: 11

How can I properly override the block blot in Quill with a span?

I am trying to use Quill as a WYSIWYG editor for creating a document with our own markdown syntax, which basically uses spans for everything but uses a data-type of the required element. It sounds complicated but it seems to be the only way we can insert liquid syntax around rows in a table.

I have managed to override the Block blot in Quill to achieve overriding the standard p elements. However, it then treats the return key with the same and adds the <br> inside a span too.

I have a codepen here, reproducible with the code below, which outputs the html below the editor container:

<html lang="en">
<head>
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css" rel="stylesheet" />
<style>
    html, body {
    font-family: arial;
    font-size: 110%;
    margin: 0;
    padding: 0;
}

body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 0;
    background-color: #f0f0f0;
}

.container {
    margin: auto;
    padding: 20px;
    position: relative;
    width: 50%;
}

#editor-container {
    width: 80%;
    margin: 20px auto;
    background-color: white;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    border-radius: 5px;
    overflow: hidden;
}

#toolbar {
    background-color: #f1f1f1;
    padding: 10px;
    border-bottom: 1px solid #ccc;
    display: flex;
    justify-content: space-around;
}

#toolbar button {
    background-color: white;
    border: 1px solid #ccc;
    padding: 5px 10px;
    cursor: pointer;
    font-size: 16px;
}

#toolbar button:hover {
    background-color: #e1e1e1;
}

#table-dropdown {
    position: relative;
}

#table-picker {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    background-color: white;
    border: 1px solid #ccc;
    padding: 10px;
    z-index: 1000;
}

#table-picker .cell {
    width: 20px;
    height: 20px;
    border: 1px solid #ccc;
    display: inline-block;
    margin: 1px;
    cursor: pointer;
}

#table-picker .cell.selected {
    background-color: #007bff;
}

[contenteditable] {
    font-size: 26px;
    padding: .25em 0em;
    margin: 1em 0;
    transition: padding .3s ease-in-out;
}

[contenteditable]:hover,
[contenteditable]:focus {
    padding: .25em;
}

[contenteditable]:hover {
    background: #fafafa;
    outline: 2px solid #eee;
}

[contenteditable]:focus {
    background: #efefef;
    outline: 2px solid blue;
}

#editor {
    padding: 20px;
    height: 300px; /* Set the desired height */
    max-height: 300px; /* Set the maximum height */
    overflow-y: auto; /* Make it scrollable if content exceeds the height */
    outline: none;
    font-size: 16px;
    line-height: 1.5;
}

.btn {
    background: #fff;
    color: darkgreen;
    border: 1px solid darkgreen;
    box-shadow: inset 0 0 0 1px;
    font-size: 1em;
    padding: .75em;
    margin-right: .5em;
}

.btn[disabled] {
    color: #666;
    border-color: #ddd;
    opacity: .5;
}

.btn:not([disabled]):hover,
.btn:not([disabled]):focus {
    outline: 3px solid;
    outline-offset: 1px;
}

.btn:not([disabled]):active {
    background: darkgreen;
    color: #fff;
}

</style>
</head>
<body>

    <div id="editor-container">
        <div id="toolbar">
            <button class="ql-bold">B</button>
            <button class="ql-italic">I</button>
            <button id="tool-save">Save</button>
            <button class="ql-table">Table</button>
        </div>
        <div id="editor"></div>
    </div>

    <h3>Generated HTML:</h3>
    <pre id="output" style="border: 1px solid #ddd; padding: 10px; min-height: 100px; white-space: pre-wrap; background: #f4f4f4;"></pre>


<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/parchment.min.js"></script>
<script>
            // Import Quill modules correctly
            const Block = Quill.import('blots/block');
        const Inline = Quill.import('blots/inline');

        // Custom Paragraph Blot (Replaces <p>)
        class CustomParagraph extends Block {
            static create() {
                let node = super.create();
                node.setAttribute('data-type', 'p'); // Replace <p> with <span data-type="p">
                return node;
            }
        }
        CustomParagraph.blotName = 'block';
        CustomParagraph.tagName = 'span'; // Use <span> instead of <p>
        Quill.register(CustomParagraph, true); // Overwrite default

        // Custom Span Blot (Replaces inline text spans)
        class CustomSpan extends Inline {
            static create() {
                let node = super.create();
                node.setAttribute('data-type', 'span');
                return node;
            }
        }
        CustomSpan.blotName = 'custom-span';
        CustomSpan.tagName = 'span';
        Quill.register(CustomSpan, true); // Overwrite default

        // Custom Table Blot (Replaces <table>)
        class CustomTable extends Block {
            static create() {
                let node = super.create();
                node.setAttribute('data-type', 'table');
                return node;
            }
        }
        CustomTable.blotName = 'custom-table';
        CustomTable.tagName = 'span';
        Quill.register(CustomTable, true); // Overwrite default

        // Custom Table Row Blot (Replaces <tr>)
        class CustomTableRow extends Block {
            static create() {
                let node = super.create();
                node.setAttribute('data-type', 'tr');
                return node;
            }
        }
        CustomTableRow.blotName = 'custom-tr';
        CustomTableRow.tagName = 'span';
        Quill.register(CustomTableRow, true); // Overwrite default

        // Initialize Quill with Overridden Defaults
        const quill = new Quill('#editor', {
            theme: 'snow',
            modules: {
                toolbar: [
                    [{ 'header': [1, 2, false] }],
                    ['bold', 'italic', 'underline'],
                    ['clean']
                ]
            },
            formats: ['custom-p', 'custom-span', 'custom-table', 'custom-tr']
        });

        // Override Quill's Clipboard Handling (Ensures pasting follows our custom format)
        quill.clipboard.addMatcher(Node.ELEMENT_NODE, (node, delta) => {
            delta.ops.forEach(op => {
                if (op.insert && typeof op.insert === 'string') {
                    op.insert = { customSpan: op.insert };
                }
            });
            return delta;
        });

        // Function to update HTML preview (display raw HTML)
        function updateHtmlOutput() {
            document.getElementById('output').textContent = quill.root.innerHTML;
        }

        // Listen for Quill changes and update output
        quill.on('text-change', updateHtmlOutput);
</script>

</body>
</html>

I have tried overriding the block blot, which has helped me to change the default paragraph for typed text, but the above issue persists with the br being wrapped in a span too.

Ultimately I want to override all of the default blots with my own - so that when I am dealing with a table, it is a span with data-type="table" etc etc.

Upvotes: 1

Views: 17

Answers (0)

Related Questions