Quill

A modern JavaScript WYSIWYG

Created: July 19th 2019
Updated: December 7th 2019
Connect with us on
image
image

Definitions

Some terms to help you navigate the post

WYSIWYG

A what you see is what you get (WYSIWYG) editor, is an editor common in CMS's, document authoring systems etc. Any system where a user needs to input formatted text.

Quill

A WYSIWYG editor written in javascript, that is easy to implement and feature loaded. See their homepage, source and their documentation for more details. You can also visit their 'playground' here.

Note that the quill library is liscenced under BSD 3-clause and is also affiliated with salesforce.

Flask

A python web microframework, designed to build simple web apps. Check out their website, or the source.

Usage

The point of this demo is to show off quill, but you could also use this demo as a starting point to buid a proper app out of.

Requirements

For this demo you will need a few things, i've outlined the requirements below and how to set them up:

  1. Flask: to get Flask you can either use pip install flask or in the folder with this readme run pip install -r requirements.txt
  2. Quill: Since quill is being demoed here I have taken the liberty of setting it up in this demo. To use in your own projects you will need to download it here

Running

  1. Install the prerequisites found in the requirements section of this readme.
  2. Run the program using python routes.py
  3. Open a web browser and go to http://localhost:5000

Whenever you hit submit you will see the output results in the terminal.

Real World Applications

The real world applications are somewhat endless, the quill website mentions linkedin, salesforce, slack and others.

Additional information

By default this demo is using the 'snow' teme for quill, check out some of the others here.

Files

File Tree

This is the organization for the files below

(root is the top level directory)

root/
├─ routes.py
└─ static/
   └── templates/
       ├─ template.html
       └─ index.html

routes.py

import os
from flask import Flask,redirect, render_template,request

app = Flask(__name__, template_folder='static/templates')

@app.route('/', methods=["GET", "POST"])
def home():
    """Primary route handler, prints the contents out from the form submit on quill-demo.html"""
    if request.method == "POST":
        print("Heading: {}".format(request.form['Heading']))
        print("Sub-Heading: {}".format(request.form['sub-heading']))
        print("Category: {}".format(request.form['category']))
        print("Content: {}".format(request.form['content']))
        return render_template("quill-demo.html")
if __name__ == '__main__':
    app.static_folder = 'static' # Allows absolute paths to folders inside /static
    app.run(host="0.0.0.0", port=5000)

/static/templates/template.html

<!DOCTYPE html>
<html>

<head>
    <!-- Manifest and Metadata-->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
    <title>&#123;% block page_title %}  &#123;% endblock %}| Markdown Writter</title>

    <!-- Stylesheets -->
    &#123;% block stylesheets %}
    &#123;% endblock %}

</head>

<body style="background-color: #f0f0f0;">

    <!-- Navigation Menu -->
    <nav class="navbar navbar-dark navbar-expand-md text-white navigation-clean" style="background-color: #141414;">
            <div class="container"><a class="navbar-brand" href="/" style="color: #f0f0f0;">Markdown Writter</a><button data-toggle="collapse" class="navbar-toggler" data-target="#navcol-1"><span class="sr-only">Toggle navigation</span><span class="navbar-toggler-icon"></span></button>
                <div
                    class="collapse navbar-collapse" id="navcol-1">
                    <ul class="nav navbar-nav ml-auto">
                        <li class="nav-item" role="presentation"><a class="nav-link active" href="#">Home</a></li>
                        <li class="nav-item" role="presentation"></li>
                        <li class="nav-item" role="presentation"><a class="nav-link" href="//github.com/WAQSS/Flask-Heroku/blob/master/README.md" target="_blank">Read Me</a></li>
                        <li class="dropdown nav-item"><a class="dropdown-toggle nav-link" data-toggle="dropdown" aria-expanded="false" href="#">Media links</a>
                            <div class="dropdown-menu" role="menu"><a class="dropdown-item" role="presentation" href="//heroku.com">Heroku Homepage</a><a class="dropdown-item" role="presentation" href="//flask.pocoo.org/docs/1.0/" target="_blank">Flask Documentation</a><a class="dropdown-item"
                                    role="presentation" href="//github.com/WAQSS/Flask-Heroku" target="_blank">Link to Repository</a></div>
                        </li>
                    </ul>
            </div>
        </div>
    </nav>


    <!-- Page Content -->

    &#123;% block content %}
    &#123;% endblock %}



    <!-- Footer -->
    <div class="text-white-50 d-xl-flex justify-content-xl-center footer-basic" style="background-color: #141414;">
        <footer class="text-center">
            <div class="text-center justify-content-xl-center align-items-xl-center social"><a href="https://github.com/WAQSS/Flask-Heroku" target="_blank"><i class="icon ion-social-github"></i></a><a href="//www.youtube.com/descent098" target="_blank"><i class="icon ion-social-youtube"></i></a><a href="//linkedin.com/in/kieran-wood/"
                    target="_blank"><i class="icon ion-social-linkedin"></i></a></div>
            <ul class="list-inline d-xl-inline-flex justify-content-xl-center align-items-xl-center">
                <li class="list-inline-item d-xl-flex"><a href="/">Home</a></li>
            </ul><a class="text-white-50 justify-content-xl-center" href="https://github.com/WAQSS/Flask-Heroku"><br><br>Powered by WAQSS<br></a></footer>
    </div>

    &#123;% block scripts %}
    <!-- Javascript -->


    &#123;% endblock %}

</body>


&#123;% block shameless_plug %}
<!--
   _____              .___        __      __.__  __  .__     
  /     \ _____     __| _/____   /  \    /  \__|/  |_|  |__  
 /  \ /  \\__  \   / __ |/ __ \  \   \/\/   /  \   __\  |  \ 
/    Y    \/ __ \_/ /_/ \  ___/   \        /|  ||  | |   Y  \
\____|__  (____  /\____ |\___  >   \__/\  / |__||__| |___|  /
        \/     \/      \/    \/         \/                \/ 
 __      __  _____   ________    _________ _________         
/  \    /  \/  _  \  \_____  \  /   _____//   _____/         
\   \/\/   /  /_\  \  /  / \  \ \_____  \ \_____  \          
 \        /    |    \/   \_/.  \/        \/        \         
  \__/\  /\____|__  /\_____\ \_/_______  /_______  /         
       \/         \/        \__>       \/        \/  
    https://github.com/WAQSS/Flask-Heroku -->
&#123;% endblock %}

</html>

/static/templates/quill-demo.html

&#123;% extends 'template.html' %}

&#123;% block page_title %} Primary Writing page &#123;% endblock page_title %}

&#123;% block stylesheets %}
    <!--Import Quill Stylesheets-->
    <link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
&#123;% endblock stylesheets %}

&#123;% block content %}
    <form class="form-horizontal" method="post">
        <fieldset>
        <!-- Form Name -->
        <legend>Demo Authoring Page</legend>
        <!-- Text input-->
        <div class="form-group">
            <label class="col-md-4 control-label" for="Heading">Heading</label>
            <div class="col-md-8">
                <input id="Heading" name="Heading" type="text" placeholder="Heading text Here" class="form-control input-md" required="">
                <span class="help-block">Enter Heading text here</span>
            </div>
        </div>
        <!-- Text input-->
        <div class="form-group">
            <label class="col-md-4 control-label" for="sub-heading">Sub-Heading</label>
            <div class="col-md-8">
                <input id="sub-heading" name="sub-heading" type="text" placeholder="Sub Heading Text here" class="form-control input-md" required="">
                <span class="help-block">Enter Sub Heading Text above</span>
            </div>
        </div>
        <!-- Text input-->
        <div class="form-group">
            <label class="col-md-4 control-label" for="category">Categorie(s)</label>
            <div class="col-md-8">
                <input id="category" name="category" type="text" placeholder="Enter Category(s) Here" class="form-control input-md" required="">
                <span class="help-block">Enter Category(s) above</span>
            </div>
        </div>
        <!--Div that will have quill injected into it-->
        <div id="editor" style="text-align: center; min-height: 800px;">
        </div>
        <!--Hidden field to be filled with value from editor div on form submit-->
        <input type="hidden" id="content" name="content">
        <!-- Submit Button -->
        <center>
            <div class="form-group">
            <label class="col-md-4 control-label" for="submit"></label>
            <div class="col-md-8">
                <button id="submit" name="submit" class="btn btn-primary">Submit</button>
            </div>
            </div>
        </center>
        </fieldset>
    </form>
    &#123;% endblock content %}
    </br>
    &#123;% block scripts %}
<!--Setting up quill editor-->
<script src="//cdn.quilljs.com/1.3.6/quill.js"></script>
<script>
    // Set up editing options available
    var toolbarOptions = [
    [{ 'font': [] }],
    [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
    ['bold', 'italic', 'underline', 'strike'],
    ['blockquote', { 'list': 'ordered'}, { 'list': 'bullet' }],
    [{ 'script': 'sub'}, { 'script': 'super' }],
    [{ 'background': [] }],
    ['clean']
    ];
    // Convert editor div to quill object
    var quill = new Quill('#editor', {
    modules: {
    toolbar: toolbarOptions // Include button in toolbar
    },
    theme: "snow"
    });
    // Adds the content inside the quill editor to the form submit
    var form = document.querySelector('form');
    form.onsubmit = function() {
    // Populate hidden form element on submit
    var content = document.querySelector('input[name=content]');
    // Get content as HTML and fill content vakye wutg it
    content.value = quill.container.firstChild.innerHTML
    console.log("Submitted", $(form).serialize(), $(form).serializeArray());
    };
</script>
&#123;% endblock scripts %}

Thank you for your support!

Be sure to share the post if it was helpful!