diff --git a/docs/advanced.rst b/docs/advanced.rst index 88e3023..eb32443 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -140,6 +140,34 @@ Bootstrap-Flask, simply use the built-in class ``SwitchField()`` instead of ``BooleanField()``. See also the example application. +.. _inputcustomization: + +Form Input Customization +------------------------ + +Rendering Label +~~~~~~~~~~~~~~~ + +Bootstrap offers control for rendering +`text `_. This is supported +for inputs of a form by adding ``render_kw={'label_class': '... ...'}`` to the +field constructor. In order to control the rendering of the label of a field, +use ``render_kw={'label_class': '... ...'}``. See also the example application. + +Rendering Radio Label +~~~~~~~~~~~~~~~~~~~~~ + +Similar support exists for the rendering of the labels of options of a +``RadioField()` with ``render_kw={'radio_class': '... ...'}``. See also the +example application. + +Rendering Description +~~~~~~~~~~~~~~~~~~~~~ + +Use ``render_kw={'descr_class': '... ...'}`` for controlling the rendering of a +field's description. See also the example application. + + .. _bootswatch_theme: Bootswatch Themes diff --git a/docs/macros.rst b/docs/macros.rst index 63f13db..78ba9a4 100644 --- a/docs/macros.rst +++ b/docs/macros.rst @@ -131,7 +131,7 @@ API form group classes, it will read the config ``BOOTSTRAP_FORM_GROUP_CLASSES`` first (the default value is ``mb-3``). -.. tip:: See :ref:`button_customization` and :ref:`checkbox_customization` to learn more on customizations. +.. tip:: See :ref:`button_customization`, :ref:`checkbox_customization` and :ref:`input_customization` to learn more on customizations. render_form() diff --git a/examples/bootstrap5/app.py b/examples/bootstrap5/app.py index acb85b2..61d39db 100644 --- a/examples/bootstrap5/app.py +++ b/examples/bootstrap5/app.py @@ -31,10 +31,10 @@ class ExampleForm(FlaskForm): """An example form that contains all the supported bootstrap style form fields.""" - date = DateField(description="We'll never share your email with anyone else.") # add help text with `description` - datetime = DateTimeField(render_kw={'placeholder': 'this is a placeholder'}) # add HTML attribute with `render_kw` + date = DateField() + datetime = DateTimeField(render_kw={'placeholder': 'this is a placeholder', 'class': 'fst-italic'}) datetime_local = DateTimeLocalField() - time = TimeField() + time = TimeField(description="This isn't shared", render_kw={'descr_class': 'fs-1 text-decoration-underline'}) month = MonthField() color = ColorField() floating = FloatField() @@ -45,7 +45,7 @@ class ExampleForm(FlaskForm): url = URLField() telephone = TelField() image = FileField(render_kw={'class': 'my-class'}, validators=[Regexp('.+\.jpg$')]) # add your class - option = RadioField(choices=[('dog', 'Dog'), ('cat', 'Cat'), ('bird', 'Bird'), ('alien', 'Alien')]) + option = RadioField(choices=[('dog', 'Dog'), ('cat', 'Cat'), ('bird', 'Bird'), ('alien', 'Alien')], render_kw={'label_class': 'text-decoration-underline', 'radio_class': 'text-decoration-line-through'}) select = SelectField(choices=[('dog', 'Dog'), ('cat', 'Cat'), ('bird', 'Bird'), ('alien', 'Alien')]) select_multiple = SelectMultipleField(choices=[('dog', 'Dog'), ('cat', 'Cat'), ('bird', 'Bird'), ('alien', 'Alien')]) bio = TextAreaField() @@ -56,16 +56,30 @@ class ExampleForm(FlaskForm): submit = SubmitField() +class ExampleFormInline(FlaskForm): + """An example inline form.""" + option = RadioField(description='Choose one', choices=[('dog', 'Dog'), ('cat', 'Cat'), ('bird', 'Bird'), ('alien', 'Alien')], render_kw={'radio_class': 'text-decoration-line-through', 'descr_class': 'fw-bold'}) + submit = SubmitField() + + +class ExampleFormHorizontal(FlaskForm): + """An example horizontal form.""" + floating = FloatField(description='a float', render_kw={'label_class': 'text-decoration-underline'}) + integer = IntegerField(description='an int', render_kw={'descr_class': 'text-decoration-line-through'}) + option = RadioField(description='choose 1', choices=[('dog', 'Dog'), ('cat', 'Cat'), ('bird', 'Bird'), ('alien', 'Alien')], render_kw={'label_class': 'text-decoration-underline'}) + submit = SubmitField() + + class HelloForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), Length(1, 20)]) password = PasswordField('Password', validators=[DataRequired(), Length(8, 150)]) - remember = BooleanField('Remember me') + remember = BooleanField('Remember me', description='Rember me on my next visit', render_kw={'descr_class': 'fw-bold text-decoration-line-through'}) submit = SubmitField() class ButtonForm(FlaskForm): username = StringField('Username', validators=[DataRequired(), Length(1, 20)]) - confirm = SwitchField('Confirmation') + confirm = SwitchField('Confirmation', description='Are you sure?', render_kw={'label_class': 'font-monospace text-decoration-underline'}) submit = SubmitField() delete = SubmitField() cancel = SubmitField() @@ -190,7 +204,9 @@ def test_form(): contact_form=ContactForm(), im_form=IMForm(), button_form=ButtonForm(), - example_form=ExampleForm() + example_form=ExampleForm(), + inline_form=ExampleFormInline(), + horizontal_form=ExampleFormHorizontal() ) diff --git a/examples/bootstrap5/templates/form.html b/examples/bootstrap5/templates/form.html index b0b7b0a..70724dc 100644 --- a/examples/bootstrap5/templates/form.html +++ b/examples/bootstrap5/templates/form.html @@ -31,6 +31,14 @@

Example Form

submit = SubmitField() {{ render_form(example_form) }} +

Inline form

+
{% raw %}{{ render_form(inline_form, form_type='inline') }}{% endraw %}
+ {{ render_form(inline_form, form_type='inline') }} + +

Horizontal form

+
{% raw %}{{ render_form(horizontal_form, form_type='horizontal') }}{% endraw %}
+ {{ render_form(horizontal_form, form_type='horizontal') }} +

Render a form with render_form

{% raw %}{{ render_form(form) }}{% endraw %}
{{ render_form(form) }} diff --git a/flask_bootstrap/templates/bootstrap5/form.html b/flask_bootstrap/templates/bootstrap5/form.html index e4d6e14..6551747 100644 --- a/flask_bootstrap/templates/bootstrap5/form.html +++ b/flask_bootstrap/templates/bootstrap5/form.html @@ -45,6 +45,23 @@ {% set form_group_classes = form_group_classes or config.BOOTSTRAP_FORM_GROUP_CLASSES %} + {# support for label_class and descr_class which are popped, to prevent they are added to input, but restored at the end of this macro for the next rendering #} + {%- set label_class = '' -%} + {%- if field.render_kw.label_class -%} + {% set label_class = field.render_kw.pop('label_class', '') -%} + {% set label_classes = ' ' + label_class -%} + {%- endif -%} + {%- set radio_class = '' -%} + {%- if field.render_kw.radio_class -%} + {% set radio_class = field.render_kw.pop('radio_class', '') -%} + {% set radio_classes = ' ' + radio_class -%} + {%- endif -%} + {%- set descr_class = '' -%} + {%- if field.render_kw.descr_class -%} + {% set descr_class = field.render_kw.pop('descr_class', '') -%} + {% set descr_classes = ' ' + descr_class -%} + {%- endif -%} + {# combine render_kw class or class/class_ argument with Bootstrap classes #} {% set render_kw_class = ' ' + field.render_kw.class if field.render_kw.class else '' %} {% set class = kwargs.pop('class', '') or kwargs.pop('class_', '') %} @@ -68,14 +85,14 @@ {%- else -%} {{ field(class="form-check-input%s" % extra_classes, **field_kwargs)|safe }} {%- endif %} - {{ field.label(class="form-check-label", for=field.id)|safe }} + {{ field.label(class="form-check-label%s" % label_classes, for=field.id)|safe }} {%- if field.errors %} {%- for error in field.errors %}
{{ error }}
{%- endfor %} {%- elif field.description -%} {% call _hz_form_wrap(horizontal_columns, form_type, required=required) %} - {{ field.description|safe }} + {{ field.description|safe }} {% endcall %} {%- endif %} @@ -85,12 +102,13 @@ this is just a hack for now, until I can think of something better #}
{%- if form_type == "inline" %} - {{ field.label(class="visually-hidden")|safe }} + {{ field.label(class="visually-hidden%s" % label_classes)|safe }} {% elif form_type == "horizontal" %} {{ field.label(class="col-form-label" + ( - " col-%s-%s" % horizontal_columns[0:2]))|safe }} + " col-%s-%s" % horizontal_columns[0:2]) + ( + "%s" % label_classes))|safe }} {%- else -%} - {{ field.label(class="form-label")|safe }} + {{ field.label(class="form-label%s" % label_classes)|safe }} {% endif %} {% if form_type == 'horizontal' %}
@@ -99,7 +117,7 @@ {% for item in field -%}
{{ item(class="form-check-input")|safe }} - {{ item.label(class="form-check-label", for=item.id)|safe }} + {{ item.label(class="form-check-label%s" % radio_classes, for=item.id)|safe }}
{% endfor %} {#% endcall %#} @@ -111,7 +129,7 @@
{{ error }}
{%- endfor %} {%- elif field.description -%} - {{ field.description|safe }} + {{ field.description|safe }} {%- endif %}
{%- elif field.type == 'SubmitField' -%} @@ -166,7 +184,9 @@ {% endif %} {%- endif %} {% elif form_type == "horizontal" %} - {{ field.label(class="col-form-label" + (" col-%s-%s" % horizontal_columns[0:2]))|safe }} + {{ field.label(class="col-form-label" + ( + " col-%s-%s" % horizontal_columns[0:2]) + ( + "%s" % label_classes))|safe }}
{%- if field.type in ['DecimalRangeField', 'IntegerRangeField'] %} {% if field.errors %} @@ -196,7 +216,7 @@ {%- endfor %} {%- elif field.description -%} {% call _hz_form_wrap(horizontal_columns, form_type, required=required) %} - {{ field.description|safe }} + {{ field.description|safe }} {% endcall %} {%- endif %} {%- else -%} @@ -225,11 +245,21 @@
{{ error }}
{%- endfor %} {%- elif field.description -%} - {{ field.description|safe }} + {{ field.description|safe }} {%- endif %} {%- endif %}
{% endif %} + + {%- if label_class -%} + {%- set _ = field.render_kw.update({'label_class': label_class}) -%} + {%- endif -%} + {%- if radio_class -%} + {%- set _ = field.render_kw.update({'radio_class': radio_class}) -%} + {%- endif -%} + {%- if descr_class -%} + {%- set _ = field.render_kw.update({'descr_class': descr_class}) -%} + {%- endif -%} {% endmacro %} {# valid form types are "basic", "inline" and "horizontal" #}