Skip to content

Commit

Permalink
feat: add markdown field widget
Browse files Browse the repository at this point in the history
Add a form field/widget to edit markdown for text fields. Implement this field initially for the `description` and `selected_bibliography` fields on the `Source` model.

Add helper tag `render_markdown` for displaying markdown fields.
  • Loading branch information
dchiller committed Jan 31, 2025
1 parent b2029ac commit a8fbfc6
Show file tree
Hide file tree
Showing 11 changed files with 546 additions and 215 deletions.
9 changes: 5 additions & 4 deletions django/cantusdb_project/main_app/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
VolpianoAreaWidget,
SelectWidget,
CheckboxWidget,
MarkdownWidget,
)

# ModelForm allows to build a form directly from a model
Expand Down Expand Up @@ -305,8 +306,8 @@ class Meta:
"date": TextInputWidget(),
"cursus": SelectWidget(),
"summary": TextAreaWidget(),
"description": TextAreaWidget(),
"selected_bibliography": TextAreaWidget(),
"description": MarkdownWidget(),
"selected_bibliography": MarkdownWidget(),
"image_link": TextInputWidget(),
"fragmentarium_id": TextInputWidget(),
"dact_id": TextInputWidget(),
Expand Down Expand Up @@ -535,8 +536,8 @@ class Meta:
"cursus": SelectWidget(),
"summary": TextAreaWidget(),
"liturgical_occasions": TextAreaWidget(),
"description": TextAreaWidget(),
"selected_bibliography": TextAreaWidget(),
"description": MarkdownWidget(),
"selected_bibliography": MarkdownWidget(),
"image_link": TextInputWidget(),
"fragmentarium_id": TextInputWidget(),
"dact_id": TextInputWidget(),
Expand Down
8 changes: 4 additions & 4 deletions django/cantusdb_project/main_app/templates/source_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ <h3>{{ source.heading }}</h3>
{% endif %}
{% if source.description %}
<dt>Description</dt>
<dd>
{{ source.description|safe|linebreaks }}
<dd class="markdown-field-display">
{{ source.description|render_markdown }}
</dd>
{% endif %}
{% if source.selected_bibliography %}
<dt>Selected Bibliography</dt>
<dd>
{{ source.selected_bibliography|safe|linebreaks }}
<dd class="markdown-field-display">
{{ source.selected_bibliography|render_markdown }}
</dd>
{% endif %}
{% if source.indexing_notes %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ <h3>
{{ form.indexing_notes }}
</div>
</div>
<div class="form-row">
<div class="row">
<div class="col-lg-1">
<button type="submit" class="btn btn-dark btn-sm" id="btn-submit">Save</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<div class="markdown-field">
<ul class="nav nav-tabs markdown-field-tabs"
id="{{ widget.name }}-tabs"
role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active edit-tab"
id="{{ widget.name }}-edit-tab"
data-bs-toggle="tab"
data-bs-target="#{{ widget.name }}-edit"
type="button"
role="tab"
aria-controls="{{ widget.name }}-edit"
aria-selected="true">Edit</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link preview-tab"
id="{{ widget.name }}-preview-tab"
data-bs-toggle="tab"
data-bs-target="#{{ widget.name }}-preview"
type="button"
role="tab"
aria-controls="{{ widget.name }}-preview"
aria-selected="false">Preview</button>
</li>
</ul>
<div class="tab-content" id="{{ widget.name }}-content">
<div class="tab-pane fade show active"
id="{{ widget.name }}-edit"
role="tabpanel"
aria-labelledby="{{ widget.name }}-edit-tab"
tabindex="0">
<textarea id="id_{{ widget.name }}"
name="{{ widget.name }}"
class="form-control form-control-sm markdown-textarea">{% if widget.value %}{{ widget.value }}{% endif %}</textarea>
</div>
<div class="tab-pane fade markdown-preview form-control form-control-sm overflow-scroll"
id="{{ widget.name }}-preview"
role="tabpanel"
aria-labelledby="{{ widget.name }}-preview-tab"
tabindex="0"></div>
</div>
</div>
12 changes: 11 additions & 1 deletion django/cantusdb_project/main_app/templatetags/helper_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from django.template.defaultfilters import stringfilter
from django.utils.safestring import mark_safe, SafeString
from django.http import HttpRequest, QueryDict
from django.utils.html import format_html_join, format_html
from django.utils.html import format_html_join
from cmarkgfm import github_flavored_markdown_to_html

from articles.models import Article
from main_app.models import Source, BaseModel
Expand Down Expand Up @@ -276,3 +277,12 @@ def join_absolute_url_links(
'<b><a href="{0}">{1}</a></b>',
((obj.get_absolute_url(), getattr(obj, display_attr)) for obj in objects),
)


@register.filter
def render_markdown(value: str) -> SafeString:
"""
Renders markdown text as HTML.
"""
html: str = github_flavored_markdown_to_html(value)
return mark_safe(html)
11 changes: 11 additions & 0 deletions django/cantusdb_project/main_app/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,14 @@ def __init__(self) -> None:

class CheckboxWidget(CheckboxInput):
pass


class MarkdownWidget(TextAreaWidget):
template_name = "widgets/markdown_widget.html"

class Media:
js = [
"https://cdn.jsdelivr.net/npm/marked/marked.min.js",
"js/markdown_widget.js",
]
css = {"all": ["stylesheets/markdown_widget.css"]}
47 changes: 47 additions & 0 deletions django/cantusdb_project/static/js/markdown_widget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
MarkdownWidget = (function () {
// Initialize the markdown widget, attaching the necessary event listeners
// to the textarea and preview elements
function init() {
var markdownFields = document.getElementsByClassName('markdown-field');
for (var i = 0; i < markdownFields.length; i++) {
let field = markdownFields[i]
let textarea = field.getElementsByClassName('markdown-textarea')[0];
let preview = field.getElementsByClassName('markdown-preview')[0];
let previewTab = field.getElementsByClassName('preview-tab')[0];
previewTab.addEventListener("show.bs.tab", function (event) {
var markdownText = textarea.value;
var parsed = marked.parse(markdownText);
preview.innerHTML = parsed;
// Set height of preview to match height of textarea
preview.style.height = textarea.clientHeight + "px";
});

textarea.addEventListener('keydown', function (e) {
if (e.key == 'Tab') {
e.preventDefault();
var start = this.selectionStart;
var end = this.selectionEnd;

// set textarea value to: text before caret + tab + text after caret
this.value = this.value.substring(0, start) +
"\t" + this.value.substring(end);

// put caret at right position again
this.selectionStart =
this.selectionEnd = start + 1;
}
});
}
}

return {
init: function () {
return init();
}
};
})();

document.addEventListener("DOMContentLoaded", function () {
MarkdownWidget.init();
});

25 changes: 25 additions & 0 deletions django/cantusdb_project/static/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,29 @@ a.display-link {
.link-hover-effect:hover {
color: #922;
text-decoration: underline;
}

.markdown-field-display table {
border-collapse: collapse;
margin-bottom: 2em;
}

.markdown-field-display table th {
text-align: center;
}

.markdown-field-display table td {
padding: 5px 15px;
}

.markdown-field-display tr:nth-child(odd) {
background-color: #e5e5e5;
}

.markdown-field-display tr:nth-child(even) {
background-color: #c4c4c4;
}

.markdown-field-display p {
text-indent: 2em;
}
31 changes: 31 additions & 0 deletions django/cantusdb_project/static/stylesheets/markdown_widget.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.markdown-field-tabs {
border-bottom: none;
}

/* Aligns top-left border of markdown-field input with tab */
.markdown-field textarea {
border-top-left-radius: 0;
height: 250px;
}

.markdown-preview,
.markdown-preview:focus {
border-style: solid none none none;
border-color: var(--bs-border-color);
border-radius: 0;
box-shadow: none;
}

.markdown-preview table {
border-collapse: collapse;
}

.markdown-preview table td {
border: 1px solid #ddd;
padding: 6px;
}

.markdown-preview table th {
border: 1px solid #ddd;
padding: 6px;
}
Loading

0 comments on commit a8fbfc6

Please sign in to comment.