Browse Source

Add Markdown preview to forms

merge-requests/68/head
wirelyre 6 years ago
committed by Deimos
parent
commit
df68d46c14
  1. 1
      tildes/scss/_base.scss
  2. 20
      tildes/scss/modules/_tab.scss
  3. 5
      tildes/scss/themes/_theme_base.scss
  4. 7
      tildes/static/js/behaviors/comment-reply-button.js
  5. 12
      tildes/static/js/behaviors/markdown-edit-tab.js
  6. 19
      tildes/static/js/behaviors/markdown-preview-tab.js
  7. 9
      tildes/static/js/behaviors/tab.js
  8. 2
      tildes/tildes/routes.py
  9. 4
      tildes/tildes/templates/intercooler/markdown_preview.jinja2
  10. 17
      tildes/tildes/templates/macros/forms.jinja2
  11. 25
      tildes/tildes/views/api/web/markdown_preview.py

1
tildes/scss/_base.scss

@ -142,6 +142,7 @@ main {
menu { menu {
list-style-type: none; list-style-type: none;
padding: 0;
} }
// We'll use lists for their semantic value sometimes, so we don't want them to // We'll use lists for their semantic value sometimes, so we don't want them to

20
tildes/scss/modules/_tab.scss

@ -1,6 +1,20 @@
// Copyright (c) 2018 Tildes contributors <code@tildes.net> // Copyright (c) 2018 Tildes contributors <code@tildes.net>
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
.tab {
font-size: 0.6rem;
}
.tab-item {
&.active .btn {
border-bottom: 2px solid;
}
.btn {
font-weight: normal;
}
}
.tab-listing-order { .tab-listing-order {
flex-wrap: nowrap; flex-wrap: nowrap;
padding-left: 0; padding-left: 0;
@ -8,8 +22,6 @@
justify-content: space-around; justify-content: space-around;
font-size: 0.6rem;
// have the tabs span the full width at small sizes // have the tabs span the full width at small sizes
width: 100%; width: 100%;
@media (min-width: $size-md) { @media (min-width: $size-md) {
@ -31,3 +43,7 @@
} }
} }
} }
.tab-markdown-mode {
display: inline-flex;
}

5
tildes/scss/themes/_theme_base.scss

@ -405,7 +405,7 @@
background-color: map-get($theme, "alert"); background-color: map-get($theme, "alert");
} }
.tab-listing-order {
.tab {
border-color: map-get($theme, "border"); border-color: map-get($theme, "border");
} }
@ -414,7 +414,8 @@
color: map-get($theme, "foreground-primary"); color: map-get($theme, "foreground-primary");
} }
&.active a {
&.active a,
&.active button {
color: map-get($theme, "link"); color: map-get($theme, "link");
border-bottom-color: map-get($theme, "link"); border-bottom-color: map-get($theme, "link");
} }

7
tildes/static/js/behaviors/comment-reply-button.js

@ -24,6 +24,7 @@ $.onmount('[data-js-comment-reply-button]', function() {
var parentCommentAuthor = $parentComment.find('header:first .link-user').text(); var parentCommentAuthor = $parentComment.find('header:first .link-user').text();
var postURL = '/api/web/comments/' + parentCommentID + '/replies'; var postURL = '/api/web/comments/' + parentCommentID + '/replies';
var markdownID = 'markdown-reply-' + parentCommentID; var markdownID = 'markdown-reply-' + parentCommentID;
var previewID = markdownID + '-preview';
if ($('#' + markdownID).length > 0) { if ($('#' + markdownID).length > 0) {
$('#' + markdownID).focus(); $('#' + markdownID).focus();
@ -39,6 +40,12 @@ $.onmount('[data-js-comment-reply-button]', function() {
clone.querySelector('span').innerHTML = 'Replying to ' + parentCommentAuthor; clone.querySelector('span').innerHTML = 'Replying to ' + parentCommentAuthor;
clone.querySelector('textarea').setAttribute('id', markdownID); clone.querySelector('textarea').setAttribute('id', markdownID);
var preview = clone.querySelector('[data-js-markdown-preview]');
preview.setAttribute('id', previewID);
clone.querySelector('[data-js-markdown-preview-tab] .btn')
.setAttribute('data-ic-target', '#' + previewID);
var cancelButton = clone.querySelector('[data-js-cancel-button]'); var cancelButton = clone.querySelector('[data-js-cancel-button]');
$(cancelButton).on('click', function (event) { $(cancelButton).on('click', function (event) {
// re-enable click/hover events on the reply button // re-enable click/hover events on the reply button

12
tildes/static/js/behaviors/markdown-edit-tab.js

@ -0,0 +1,12 @@
// Copyright (c) 2018 Tildes contributors <code@tildes.net>
// SPDX-License-Identifier: AGPL-3.0-or-later
$.onmount('[data-js-markdown-edit-tab]', function() {
$(this).click(function(event) {
var $editTextarea = $(this).closest('form').find('[name="markdown"]');
var $previewDiv = $(this).closest('form').find('[data-js-markdown-preview]');
$editTextarea.removeClass('d-none');
$previewDiv.addClass('d-none');
});
});

19
tildes/static/js/behaviors/markdown-preview-tab.js

@ -0,0 +1,19 @@
// Copyright (c) 2018 Tildes contributors <code@tildes.net>
// SPDX-License-Identifier: AGPL-3.0-or-later
$.onmount('[data-js-markdown-preview-tab]', function() {
$(this).click(function(event) {
var $editTextarea = $(this).closest('form').find('[name="markdown"]');
var $previewDiv = $(this).closest('form').find('[data-js-markdown-preview]');
$editTextarea.addClass('d-none');
$previewDiv.removeClass('d-none');
});
$(this).on('after.success.ic success.ic', function(event) {
// Stop intercooler event from bubbling up past this button. This
// prevents behaviors on parent elements from mistaking a successful
// "preview" from a successful "submit".
event.stopPropagation();
});
});

9
tildes/static/js/behaviors/tab.js

@ -0,0 +1,9 @@
// Copyright (c) 2018 Tildes contributors <code@tildes.net>
// SPDX-License-Identifier: AGPL-3.0-or-later
$.onmount('[data-js-tab]', function() {
$(this).click(function(event) {
$(this).siblings().removeClass('active');
$(this).addClass('active');
});
});

2
tildes/tildes/routes.py

@ -154,6 +154,8 @@ def add_intercooler_routes(config: Configurator) -> None:
) )
add_ic_route("user_ban", "/ban", factory=user_by_username) add_ic_route("user_ban", "/ban", factory=user_by_username)
add_ic_route("markdown_preview", "/markdown_preview", factory=LoggedInFactory)
class LoggedInFactory: class LoggedInFactory:
"""Simple class to use as `factory` to restrict routes to logged-in users. """Simple class to use as `factory` to restrict routes to logged-in users.

4
tildes/tildes/templates/intercooler/markdown_preview.jinja2

@ -0,0 +1,4 @@
{# Copyright (c) 2018 Tildes contributors <code@tildes.net> #}
{# SPDX-License-Identifier: AGPL-3.0-or-later #}
{{ rendered_html|safe }}

17
tildes/tildes/templates/macros/forms.jinja2

@ -6,6 +6,21 @@
<span>{{ caption }}</span> <span>{{ caption }}</span>
<a href="https://docs.tildes.net/text-formatting" target="_blank" tabindex="-1">Formatting help</a> <a href="https://docs.tildes.net/text-formatting" target="_blank" tabindex="-1">Formatting help</a>
</label> </label>
<div class="form-markdown">
<menu class="tab tab-markdown-mode">
<li class="tab-item active" data-js-tab data-js-markdown-edit-tab>
<button type="button" class="btn btn-link">Edit</button>
</li>
<li class="tab-item" data-js-tab data-js-markdown-preview-tab>
<button
type="button"
class="btn btn-link"
data-ic-post-to="/api/web/markdown_preview"
data-ic-target="#{{ id }}-preview"
data-ic-replace-target="false"
>Preview</button>
</li>
</menu>
<textarea <textarea
class="form-input" class="form-input"
id="{{ id }}" id="{{ id }}"
@ -14,6 +29,8 @@
data-js-ctrl-enter-submit-form data-js-ctrl-enter-submit-form
{% if auto_focus %}data-js-auto-focus{% endif %} {% if auto_focus %}data-js-auto-focus{% endif %}
>{% if text %}{{ text }}{% endif %}</textarea> >{% if text %}{{ text }}{% endif %}</textarea>
<div id="{{ id }}-preview" class="d-none" data-js-markdown-preview></div>
</div>
{% endmacro %} {% endmacro %}
{% macro topic_tagging(value=None, auto_focus=False, autocomplete_options=None) %} {% macro topic_tagging(value=None, auto_focus=False, autocomplete_options=None) %}

25
tildes/tildes/views/api/web/markdown_preview.py

@ -0,0 +1,25 @@
# Copyright (c) 2018 Tildes contributors <code@tildes.net>
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Web API endpoint for previewing Markdown."""
from pyramid.request import Request
from webargs.pyramidparser import use_kwargs
from tildes.lib.markdown import convert_markdown_to_safe_html
from tildes.schemas.comment import CommentSchema
from tildes.views.decorators import ic_view_config
@ic_view_config(
route_name="markdown_preview",
request_method="POST",
renderer="markdown_preview.jinja2",
)
@use_kwargs(CommentSchema(only=("markdown",)))
def markdown_preview(request: Request, markdown: str) -> dict:
"""Render the provided text as Markdown."""
# pylint: disable=unused-argument
rendered_html = convert_markdown_to_safe_html(markdown)
return {"rendered_html": rendered_html}
Loading…
Cancel
Save