Started working on Bootstrap5 theme.

This commit is contained in:
Benedikt Willi 2023-09-29 17:10:32 +02:00
parent d5eda65764
commit 481a4b77e4
39 changed files with 23531 additions and 7 deletions

View file

@ -12,8 +12,8 @@ RUN apt-get update && \
nano \
chromium \
graphviz \
libpq-dev \
python3.9
libpq-dev
# python3.9
RUN pip install pip --upgrade
RUN pip install virtualenv

View file

@ -21,8 +21,8 @@ def gettext(s):
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
DEBUG = False
DEBUG_TOOLBAR = False
DEBUG = True
DEBUG_TOOLBAR = True
DEV = False
ADMINS = (
@ -311,6 +311,10 @@ INSTALLED_APPS = [
# ***********************************************************************
"fobi.contrib.themes.simple", # Simple theme
# ***********************************************************************
# ************************* Bootstrap 5 theme ***************************
# ***********************************************************************
"fobi.contrib.themes.bootstrap5", # Bootstrap 5 theme
# ***********************************************************************
# ***********************************************************************
# ************************* Fobi form importers *************************
# ***********************************************************************

View file

@ -0,0 +1,18 @@
from .base import *
INSTALLED_APPS = list(INSTALLED_APPS)
try:
INSTALLED_APPS.remove(
"admin_tools"
) if "admin_tools" in INSTALLED_APPS else None
INSTALLED_APPS.remove(
"admin_tools.menu"
) if "admin_tools.menu" in INSTALLED_APPS else None
INSTALLED_APPS.remove(
"admin_tools.dashboard"
) if "admin_tools.dashboard" in INSTALLED_APPS else None
except Exception as err:
pass
FOBI_DEFAULT_THEME = "bootstrap5"

View file

@ -3,6 +3,8 @@ from selenium import webdriver
from .bootstrap3_theme import *
from .bootstrap5_theme import *
def project_dir(base):
return os.path.abspath(
@ -16,7 +18,7 @@ def gettext(s):
PROJECT_DIR = project_dir
DEBUG = True
DEBUG_TOOLBAR = False
DEBUG_TOOLBAR = True
DEBUG_TEMPLATE = True
# TEMPLATE_DEBUG = True
DEV = True
@ -57,7 +59,11 @@ MIGRATION_MODULES = {
"page": "page.migrations",
}
INTERNAL_IPS = ("127.0.0.1",)
INTERNAL_IPS = ["127.0.0.1",]
if DEBUG:
import socket # only if you haven't already imported this
hostname, _, ips = socket.gethostbyname_ex(socket.gethostname())
INTERNAL_IPS = [ip[: ip.rfind(".")] + ".1" for ip in ips] + ["127.0.0.1", "10.0.2.2"]
ALLOWED_HOSTS = ["*"]
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"

View file

@ -0,0 +1,22 @@
fobi.contrib.themes.bootstrap5
------------------------------
A ``django-fobi`` Bootstrap 5 theme.
Installation
~~~~~~~~~~~~
(1) Add ``fobi.contrib.themes.bootstrap5`` to the
``INSTALLED_APPS`` in your ``settings.py``.
.. code-block:: python
INSTALLED_APPS = (
# ...
'fobi.contrib.themes.bootstrap5',
# ...
)
(2) Specify ``bootstrap5`` as a default theme in your ``settings.py``:
.. code-block:: python
FOBI_DEFAULT_THEME = 'bootstrap5'

View file

@ -0,0 +1,10 @@
__title__ = "fobi.contrib.themes.bootstrap5"
__license__ = "GPL 2.0/LGPL 2.1"
__all__ = (
"default_app_config",
"UID",
)
default_app_config = "fobi.contrib.themes.bootstrap5.apps.Config"
UID = "bootstrap5"

View file

@ -0,0 +1,12 @@
from django.apps import AppConfig
__title__ = "fobi.contrib.themes.bootstrap3.apps"
__license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("Config",)
class Config(AppConfig):
"""Config."""
name = "fobi.contrib.themes.bootstrap5"
label = "fobi_contrib_themes_bootstrap5"

View file

@ -0,0 +1,108 @@
from django.utils.translation import gettext_lazy as _
from fobi.base import BaseTheme, theme_registry
from . import UID
__title__ = "fobi.contrib.themes.bootstrap5.theme"
__license__ = "GPL 2.0/LGPL 2.1"
__all__ = ("Bootstrap5Theme",)
class Bootstrap5Theme(BaseTheme):
"""Bootstrap5 theme."""
uid = UID
name = _("Bootstrap 5")
media_css = (
"bootstrap5/css/bootstrap.min.css",
"bootstrap5/css/bootstrap5_fobi_extras.css",
# "css/fobi.core.css",
)
media_js = (
"bootstrap5/js/bootstrap.min.js",
"js/jquery-1.10.2.min.js",
"js/jquery.slugify.js",
"js/fobi.core.js",
"bootstrap5/js/bootstrap5_fobi_extras.js", # Theme-specific scripts
)
# ***********************************************************************
# ***********************************************************************
# **************************** Templates ********************************
# ***********************************************************************
# ***********************************************************************
# ***********************************************************************
# *************************** Base templates ****************************
# ***********************************************************************
master_base_template = "bootstrap5/_base.html"
base_template = "bootstrap5/base.html"
# ***********************************************************************
# ***************************** Snippets ********************************
# ***********************************************************************
form_snippet_template_name = "bootstrap5/snippets/form_snippet.html"
form_properties_snippet_template_name = (
"bootstrap5/snippets/form_properties_snippet.html"
)
messages_snippet_template_name = "bootstrap3/snippets/messages_snippet.html"
form_non_field_and_hidden_errors_snippet_template = (
"bootstrap5/snippets/form_non_field_and_hidden_errors_snippet.html"
)
form_ajax = "bootstrap5/snippets/form_ajax.html"
form_wizard_ajax = "bootstrap5/snippets/form_wizard_ajax.html"
form_wizard_snippet_template_name = (
"bootstrap5/snippets/form_wizard_snippet.html"
)
form_wizard_properties_snippet_template_name = (
"bootstrap5/snippets/form_wizard_properties_snippet.html"
)
# ***********************************************************************
# **************************** Form entry CRUD **************************
# ***********************************************************************
create_form_entry_template = "bootstrap5/create_form_entry.html"
create_form_entry_ajax_template = "bootstrap3/create_form_entry_ajax.html"
edit_form_entry_template = "bootstrap3/edit_form_entry.html"
edit_form_entry_ajax_template = "bootstrap3/edit_form_entry_ajax.html"
form_entry_submitted_template = "bootstrap3/form_entry_submitted.html"
form_entry_submitted_ajax_template = (
"bootstrap3/form_entry_submitted_ajax.html"
)
embed_form_entry_submitted_ajax_template = (
"bootstrap3/embed_form_entry_submitted_ajax.html"
)
view_form_entry_template = "bootstrap3/view_form_entry.html"
view_form_entry_ajax_template = "bootstrap3/view_form_entry_ajax.html"
view_embed_form_entry_ajax_template = (
"bootstrap3/view_embed_form_entry_ajax.html"
)
def __init__(self, user=None):
"""Constructor."""
super(Bootstrap5Theme, self).__init__(user=user)
# ***********************************************************************
# ***************************** Dashboard *******************************
# ***********************************************************************
dashboard_template = "bootstrap5/dashboard.html"
form_wizards_dashboard_template = "bootstrap3/form_wizards_dashboard.html"
forms_list_template = "bootstrap3/forms_list.html"
theme_registry.register(Bootstrap5Theme)

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,203 @@
{% load i18n static %}
<!DOCTYPE html>
<html lang="en"{% block html-attributes %}{% endblock html-attributes %}>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
{% block favicon %}
<link rel="shortcut icon" href="{% block favicon-image %}{% endblock favicon-image %}">
{% endblock favicon %}
<title>{% block page-title %}{% endblock page-title %} | {% block site-title %}{% endblock site-title %}</title>
{% block stylesheets %}
{#<!-- Additional core CSS files that somehow can't be put into the python theme -->#}
{% endblock stylesheets %}
{% block theme-stylesheets %}
{#<!-- This is where stylesheets declared in the Python theme are listed -->#}
{% for css in fobi_theme.get_media_css %}
<link href="{{ css }}" rel="stylesheet" media="all" />
{% endfor %}
{% endblock theme-stylesheets %}
{% block extra-stylesheets %}{% endblock extra-stylesheets %}
{% block head-javascripts %}
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->
{% endblock head-javascripts %}
{% block extra-head-javascripts %}{% endblock extra-head-javascripts %}
{% block extrahead %}{% endblock extrahead %}
</head>
<body{% block body-attrs %}{% if fobi_theme %} class="theme {{ fobi_theme.html_class }}"{% endif %}{% endblock body-attrs %}>
{% block before-main-wrapper %}{% endblock %}
{% block main-wrapper %}
{% block navbar-wrapper %}
<div class="{% block navbar-class %}navbar navbar-expand-lg navbar-fixed-top bg-body-tertiary{% endblock navbar-class %}" role="navigation">
{% block navbar %}
<div class="{% block navbar-container-class %}container{% endblock navbar-container-class %}">
{% block navbar-content %}
{% block navbar-header %}
<div class="navbar-header">
<button type="button" class="navbar-toggler" data-bs-toggle="collapse" data-bs-target="#navbarCollapse">
<span class="navbar-toggler-icon"></span>
</button>
{% block dashboard-menu-item %}
<a class="navbar-brand" href="{% url 'fobi.dashboard' %}">{{ fobi_theme.project_name }}</a>
{% endblock dashboard-menu-item %}
</div>
{% endblock navbar-header %}
{% block navbar-menu-wrapper %}
<div class="collapse navbar-collapse" id="navbarCollapse">
{% block navbar-menu %}
<ul class="navbar-nav">
{% block navbar-menu-content %}
<!--
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
Dropdown <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="#sub1">Submenu 1</a></li>
<li><a href="#sub2">Submenu 2</a></li>
</ul>
</li>
-->
{% endblock navbar-menu-content %}
{% block navbar-menu-navigation-content %}
{% comment %}
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
{% trans "Dashboards" %} <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="{% url 'fobi.dashboard' %}">{% trans "Forms dashboard" %}</a></li>
<li><a href="{% url 'fobi.form_wizards_dashboard' %}">{% trans "Form wizards dashboard" %}</a></li>
</ul>
</li>
{% endcomment %}
{% endblock navbar-menu-navigation-content %}
</ul>
{% endblock navbar-menu %}
{% block navbar-menu-right %}
<ul class="navbar-nav navbar-right ms-md-auto">
{% block navbar-menu-right-content %}
<li class="menu-item"><a class="menu-link" href="{% url 'fobi.dashboard' %}">{% trans "Forms" %}</a></li>
<li class="menu-item"><a class="menu-link" href="{% url 'fobi.form_wizards_dashboard' %}">{% trans "Wizards" %}</a></li>
{% endblock navbar-menu-right-content %}
</ul>
{% endblock navbar-menu-right %}
</div><!-- /.nav-collapse -->
{% endblock navbar-menu-wrapper %}
{% endblock navbar-content %}
</div><!-- /.container -->
{% endblock navbar %}
</div><!-- /.navbar -->
{% endblock navbar-wrapper %}
{% block main-content-wrapper %}
<div class="container">
{% block messages-wrapper %}
{% include fobi_theme.messages_snippet_template_name %}
{% endblock messages-wrapper %}
{% block main-content %}
<div class="row">
<div{#% block main-content-inner-attrs %#} class="col-12 col-sm-9"{#% endblock main-content-inner-attrs %#}>
{% block responsive-nav %}
<p class="float-end d-block d-sm-none">
<button type="button" class="btn btn-primary btn-sm" data-toggle="offcanvas">Toggle nav</button>
</p>
{% endblock responsive-nav %}
{% block content-wrapper %}
<div class="row">
<div class="{% block content-class %}col-lg-12{% endblock content-class %}">
{% block content %}
<!--
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p>
<p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
-->
{% endblock content %}
</div><!--/span-->
</div><!--/row-->
{% endblock content-wrapper %}
</div><!--/span-->
{% block sidebar-wrapper %}
<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar" role="navigation">
{% block sidebar %}
<div class="list-group">
{% block sidebar-content %}
<!--
<a href="#" class="list-group-item active">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
-->
{% endblock sidebar-content %}
</div>
{% endblock sidebar %}
</div><!--/span-->
{% endblock sidebar-wrapper %}
</div><!--/row-->
{% endblock main-content %}
{% block content-footer-separator %}
<hr>
{% endblock content-footer-separator %}
{% block footer-wrapper %}
<footer>
{% block footer %}
<p>{% block footer-content %}{{ fobi_theme.footer_text|safe }}{% endblock footer-content %}</p>
{% endblock footer %}
</footer>
{% endblock footer-wrapper %}
</div><!--/.container-->
{% endblock main-content-wrapper %}
{% endblock main-wrapper %}
{% block javascripts %}
{#<!-- Theme core JavaScript that somehow can't be put into the Python theme -->#}
{#<!-- Placed at the end of the document so the pages load faster, although might be changed, if plugins need it to be otherwise -->#}
{% endblock javascripts %}
{% block theme-javascripts %}
{#<!-- This is where javascripts declared in the Python theme are listed -->#}
{% for js in fobi_theme.get_media_js %}
<script src="{{ js }}"></script>
{% endfor %}
{% endblock theme-javascripts %}
</body>
</html>

View file

@ -0,0 +1 @@
{% extends "bootstrap5/_base.html" %}

View file

@ -0,0 +1,21 @@
{% extends "fobi/generic/create_form_entry.html" %}
{% load i18n %}
{% block page-title %}{% trans "Create form entry" %}{% endblock page-title %}
{% block navbar-menu-content %}
{% endblock navbar-menu-content %}
{% block navbar-menu-right-content %}
<li class="nav-item"><a class="nav-link" href="{% url 'fobi.dashboard' %}">{% trans "Forms" %}</a></li>
<li class="nav-item"><a class="nav-link acitve" href="{% url 'fobi.form_wizards_dashboard' %}">{% trans "Wizards" %}</a></li>
{% endblock navbar-menu-right-content %}
{% block content %}
{% include fobi_theme.create_form_entry_ajax_template %}
{% endblock content %}
{% block sidebar-wrapper %}
{% endblock sidebar-wrapper %}

View file

@ -0,0 +1 @@
{% extends "fobi/generic/create_form_entry_ajax.html" %}

View file

@ -0,0 +1,7 @@
{% extends "fobi/generic/dashboard.html" %}
{% load i18n %}
{% block navbar-menu-right-content %}
<li class="nav-item"><a class="nav-link active" href="{% url 'fobi.dashboard' %}">{% trans "Forms" %}</a></li>
<li class="nav-item"><a class="nav-link" href="{% url 'fobi.form_wizards_dashboard' %}">{% trans "Wizards" %}</a></li>
{% endblock navbar-menu-right-content %}

View file

@ -0,0 +1,11 @@
{% extends "fobi/generic/snippets/form_ajax.html" %}
{% block form_page_header_html_class %}page-header{% endblock %}
{% block form_html_class %}form-horizontal{% endblock %}
{% block form_button_outer_wrapper_html_class %}control-group{% endblock %}
{% block form_button_wrapper_html_class %}controls{% endblock %}
{% block form_primary_button_html_class %}btn btn-primary{% endblock %}

View file

@ -0,0 +1,16 @@
{% load i18n %}
{% for error_message in form.non_field_errors %}
<div class="alert alert-danger">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class="sr-only">{% trans "Error" %}:</span>
{{ error_message.as_text }}
</div>
{% endfor %}
{% for error_field_name, error_message in form_hidden_fields_errors.items %}
<div class="alert alert-danger">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class="sr-only">{{ error_field_name }}:</span>
{{ error_field_name }}: {{ error_message.as_text }}
</div>
{% endfor %}

View file

@ -0,0 +1,23 @@
{% extends "fobi/generic/snippets/form_properties_snippet.html" %}
{% block form_required_fields_message_wrapper_class %}well well-sm{% endblock %}
{% block non_field_errors_form_element_wrapper_html_class %}form-group{% endblock %}
{% block form_element_wrapper_hidden_html_class %}hidden{% endblock %}
{% block form_element_wrapper_html_class %}form-group{% endblock %}
{% block form_element_wrapper_error_html_class %}has-error{% endblock %}
{% block form_checkbox_element_field_wrapper_html_class %}col-sm-offset-2 col-sm-9{% endblock %}
{% block form_element_label_html_class %}col-sm-3 control-label{% endblock %}
{% block form_element_required_field_sign_html_class %}required-field{% endblock %}
{% block form_element_field_wrapper_html_class %}col-sm-9{% endblock %}
{% block form_element_error_html_class %}help-block has-error{% endblock %}
{% block form_element_help_text_html_class %}help-block{% endblock %}

View file

@ -0,0 +1,32 @@
{% extends "fobi/generic/snippets/form_snippet.html" %}
{% load i18n fobi_tags %}
{% block form_required_fields_message_wrapper_class %}bg-light border rounded p-1 ps-3 pe-3 mb-3{% endblock %}
{% block form_element_wrapper_hidden_html_class %}d-none{% endblock %}
{% block form_element_wrapper_html_class %}mb-3 row{% endblock %}
{% block form_element_wrapper_error_html_class %}has-error{% endblock %}
{% block form_element_label_html_class %}col-sm-3 col-form-label{% endblock %}
{% block form_element_required_field_sign_html_class %}required-field{% endblock %}
{% block form_element_field_wrapper_html_class %}col-sm-9{% endblock %}
{% block form_element_error_html_class %}help-block invalid-feedback{% endblock %}
{% block form_element_help_text_html_class %}form-text{% endblock %}
{% block form_element_field %}
{% get_form_field_type field as form_field_type %}
{% if form_field_type.is_text %}
{{ field|add_class:"form-control" }}
{% elif form_field_type.is_textarea %}
{{ field|add_class:"form-control" }}
{% else %}
{{ field }}
{% endif %}
{% endblock form_element_field %}

View file

@ -0,0 +1 @@
{% extends "fobi/generic/snippets/form_wizard.html" %}

View file

@ -0,0 +1,51 @@
{% extends "fobi/generic/snippets/form_wizard_ajax.html" %}
{% load i18n %}
{% block form_page_header_html_class %}page-header{% endblock %}
{% block form_html_class %}form-horizontal{% endblock %}
{% block form_button_outer_wrapper_html_class %}control-group{% endblock %}
{% block form_button_wrapper_html_class %}controls{% endblock %}
{% block form_primary_button_html_class %}btn btn-primary{% endblock %}
{% block form_wizard_previous_button_html_class %}btn btn-primary{% endblock %}
{% block form_wizard_first_button_html_class %}btn btn-primary{% endblock %}
{% block form_wizard_first_button_text %}{% trans "First" %}{% endblock %}
{% block form_wizard_primary_button_text %}{% if wizard.steps.is_last_step %}{% trans "Submit" %}{% else %}{% trans "Next" %}{% endif %}{% endblock %}
{% block form_wizard_previous_button_text %}{% trans "Previous" %}{% endblock %}
{% block form_page_sub_title_wrapper %}
<nav aria-label="...">
<ul class="pagination">
<li class="disabled">
<span>
<span aria-hidden="true">&laquo;</span>
</span>
</li>
{% for step_index in steps_range %}
{% if step_index == wizard.steps.step1 %}
<li class="active">
<span>{{ step_index }} <span class="sr-only">{% trans "(current)" %}</span></span>
</li>
{% else %}
<li class="disabled">
<span>
<span aria-hidden="true">{{ step_index }}</span>
</span>
</li>
{% endif %}
{% endfor %}
<li class="disabled">
<span>
<span aria-hidden="true">&raquo;</span>
</span>
</li>
</ul>
</nav>
{% endblock form_page_sub_title_wrapper %}

View file

@ -0,0 +1,21 @@
{% extends "fobi/generic/snippets/form_wizard_properties_snippet.html" %}
{% block form_required_fields_message_wrapper_class %}well well-sm{% endblock %}
{% block non_field_errors_form_element_wrapper_html_class %}form-group{% endblock %}
{% block form_element_wrapper_hidden_html_class %}hidden{% endblock %}
{% block form_element_wrapper_html_class %}form-group{% endblock %}
{% block form_checkbox_element_field_wrapper_html_class %}col-sm-offset-2 col-sm-9{% endblock %}
{% block form_element_label_html_class %}col-sm-3 control-label{% endblock %}
{% block form_element_required_field_sign_html_class %}required-field{% endblock %}
{% block form_element_field_wrapper_html_class %}col-sm-9{% endblock %}
{% block form_element_error_html_class %}help-block has-error{% endblock %}
{% block form_element_help_text_html_class %}help-block{% endblock %}

View file

@ -0,0 +1,21 @@
{% extends "fobi/generic/snippets/form_wizard_snippet.html" %}
{% load i18n fobi_tags %}
{% block form_required_fields_message_wrapper_class %}well well-sm{% endblock %}
{% block form_element_wrapper_hidden_html_class %}hidden{% endblock %}
{% block form_element_wrapper_html_class %}form-group{% endblock %}
{% block form_element_wrapper_error_html_class%}has-error{% endblock %}
{% block form_element_label_html_class %}col-sm-3 control-label{% endblock %}
{% block form_element_required_field_sign_html_class %}required-field{% endblock %}
{% block form_element_field_wrapper_html_class %}col-sm-9{% endblock %}
{% block form_element_error_html_class %}help-block has-error{% endblock %}
{% block form_element_help_text_html_class %}help-block{% endblock %}

View file

@ -0,0 +1 @@
{% extends "fobi/generic/snippets/messages_snippet.html" %}

View file

@ -54,7 +54,9 @@
</div>
{% endfor %}
{% else %}
{{ field }}
{% block form_element_field %}
{{ field }}
{% endblock form_element_field %}
{% endif %}
{% if field.errors %}

View file

@ -20,6 +20,7 @@ __all__ = (
"has_edit_form_entry_permissions",
"render_auth_link",
"render_fobi_forms_list",
"add_class",
)
THEME = get_theme(request=None, as_instance=True)
@ -443,6 +444,15 @@ class GetFormFieldTypeNode(Node):
if isinstance(field.field.widget, forms.RadioSelect):
properties.append("is_radio")
if isinstance(field.field.widget, forms.TextInput):
properties.append("is_text")
if isinstance(field.field.widget, forms.Textarea):
properties.append("is_textarea")
if isinstance(field.field.widget, forms.HiddenInput):
properties.append("is_hidden")
res = FormFieldType(properties)
context[self.as_var] = res
@ -537,3 +547,18 @@ def get_form_hidden_fields_errors(parser, token):
form = parser.compile_filter(bits[1])
return GetFormHiddenFieldsErrorsNode(form=form, as_var=as_var)
@register.filter
def add_class(value, arg):
"""Add class to form field.
:example:
{{ form.field|add_class:"form-control" }}
"""
new_class = value.field.widget.attrs.get('class', '')
if new_class:
new_class += ' '
new_class += arg
return value.as_widget(attrs={'class': new_class})