Some terms to help you navigate the post
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.
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.
A python web microframework, designed to build simple web apps. Check out their website, or the source.
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.
For this demo you will need a few things, i've outlined the requirements below and how to set them up:
pip install flask
or in the folder with this readme run pip install -r requirements.txt
python routes.py
Whenever you hit submit you will see the output results in the terminal.
The real world applications are somewhat endless, the quill website mentions linkedin, salesforce, slack and others.
By default this demo is using the 'snow' teme for quill, check out some of the others here.
This is the organization for the files below
(root is the top level directory)
root/
├─ routes.py
└─ static/
└── templates/
├─ template.html
└─ index.html
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)
<!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>{% block page_title %} {% endblock %}| Markdown Writter</title>
<!-- Stylesheets -->
{% block stylesheets %}
{% 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 -->
{% block content %}
{% 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>
{% block scripts %}
<!-- Javascript -->
{% endblock %}
</body>
{% block shameless_plug %}
<!--
_____ .___ __ __.__ __ .__
/ \ _____ __| _/____ / \ / \__|/ |_| |__
/ \ / \\__ \ / __ |/ __ \ \ \/\/ / \ __\ | \
/ Y \/ __ \_/ /_/ \ ___/ \ /| || | | Y \
\____|__ (____ /\____ |\___ > \__/\ / |__||__| |___| /
\/ \/ \/ \/ \/ \/
__ __ _____ ________ _________ _________
/ \ / \/ _ \ \_____ \ / _____// _____/
\ \/\/ / /_\ \ / / \ \ \_____ \ \_____ \
\ / | \/ \_/. \/ \/ \
\__/\ /\____|__ /\_____\ \_/_______ /_______ /
\/ \/ \__> \/ \/
https://github.com/WAQSS/Flask-Heroku -->
{% endblock %}
</html>
{% extends 'template.html' %}
{% block page_title %} Primary Writing page {% endblock page_title %}
{% block stylesheets %}
<!--Import Quill Stylesheets-->
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
{% endblock stylesheets %}
{% 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>
{% endblock content %}
</br>
{% 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>
{% endblock scripts %}