Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
flask-admin
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Python-Dev
flask-admin
Commits
c5f44b10
Unverified
Commit
c5f44b10
authored
Apr 22, 2020
by
Serge S. Koval
Committed by
GitHub
Apr 22, 2020
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1926 from icaicai/bootstrap4
Bootstrap4
parents
58c384d4
28c140c5
Changes
10
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
898 additions
and
33 deletions
+898
-33
rules.py
flask_admin/form/rules.py
+140
-0
bs4_modal.js
flask_admin/static/admin/js/bs4_modal.js
+5
-0
select2-bootstrap4.css
flask_admin/static/vendor/select2/select2-bootstrap4.css
+6
-1
bootstrap4-editable.css
...dmin/static/vendor/x-editable/css/bootstrap4-editable.css
+663
-0
bootstrap4-editable.min.js
...in/static/vendor/x-editable/js/bootstrap4-editable.min.js
+7
-0
base.html
flask_admin/templates/bootstrap4/admin/base.html
+2
-2
lib.html
flask_admin/templates/bootstrap4/admin/lib.html
+43
-15
list.html
flask_admin/templates/bootstrap4/admin/model/list.html
+1
-1
create.html
...admin/templates/bootstrap4/admin/model/modals/create.html
+18
-6
edit.html
...k_admin/templates/bootstrap4/admin/model/modals/edit.html
+13
-8
No files found.
flask_admin/form/rules.py
View file @
c5f44b10
...
...
@@ -359,6 +359,143 @@ class FieldSet(NestedRule):
super
(
FieldSet
,
self
)
.
__init__
(
rule_set
,
separator
=
separator
)
class
Row
(
NestedRule
):
def
__init__
(
self
,
*
columns
,
**
kw
):
super
(
Row
,
self
)
.
__init__
()
self
.
rules
=
columns
def
__call__
(
self
,
form
,
form_opts
=
None
,
field_args
=
{}):
cols
=
[]
for
col
in
self
.
rules
:
if
col
.
visible_fields
:
w_args
=
form_opts
.
widget_args
.
setdefault
(
col
.
visible_fields
[
0
],
{})
w_args
.
setdefault
(
'column_class'
,
'col'
)
cols
.
append
(
col
(
form
,
form_opts
,
field_args
))
return
Markup
(
'<div class="form-row">
%
s</div>'
%
''
.
join
(
cols
))
class
Group
(
Macro
):
def
__init__
(
self
,
field_name
,
prepend
=
None
,
append
=
None
,
**
kwargs
):
'''
Bootstrap Input group.
'''
render_field
=
kwargs
.
get
(
'render_field'
,
'lib.render_field'
)
super
(
Group
,
self
)
.
__init__
(
render_field
)
self
.
field_name
=
field_name
self
.
_addons
=
[]
if
prepend
:
if
not
isinstance
(
prepend
,
(
tuple
,
list
)):
prepend
=
[
prepend
]
for
cnf
in
prepend
:
if
isinstance
(
cnf
,
str
):
self
.
_addons
.
append
({
'pos'
:
'prepend'
,
'type'
:
'text'
,
'text'
:
cnf
})
continue
if
cnf
[
'type'
]
in
(
'field'
,
'html'
,
'text'
):
cnf
[
'pos'
]
=
'prepend'
self
.
_addons
.
append
(
cnf
)
if
append
:
if
not
isinstance
(
append
,
(
tuple
,
list
)):
append
=
[
append
]
for
cnf
in
append
:
if
isinstance
(
cnf
,
str
):
self
.
_addons
.
append
({
'pos'
:
'append'
,
'type'
:
'text'
,
'text'
:
cnf
})
continue
if
cnf
[
'type'
]
in
(
'field'
,
'html'
,
'text'
):
cnf
[
'pos'
]
=
'append'
self
.
_addons
.
append
(
cnf
)
print
(
self
.
_addons
)
@
property
def
visible_fields
(
self
):
fields
=
[
self
.
field_name
]
for
cnf
in
self
.
_addons
:
if
cnf
[
'type'
]
==
'field'
:
fields
.
append
(
cnf
[
'name'
])
return
fields
def
__call__
(
self
,
form
,
form_opts
=
None
,
field_args
=
{}):
"""
Render field.
:param form:
Form object
:param form_opts:
Form options
:param field_args:
Optional arguments that should be passed to template or the field
"""
field
=
getattr
(
form
,
self
.
field_name
,
None
)
if
field
is
None
:
raise
ValueError
(
'Form
%
s does not have field
%
s'
%
(
form
,
self
.
field_name
))
if
form_opts
:
widget_args
=
form_opts
.
widget_args
else
:
widget_args
=
{}
opts
=
{}
prepend
=
[]
append
=
[]
for
cnf
in
self
.
_addons
:
ctn
=
None
typ
=
cnf
[
'type'
]
if
typ
==
'field'
:
name
=
cnf
[
'name'
]
fld
=
form
.
_fields
.
get
(
name
,
None
)
if
fld
:
w_args
=
widget_args
.
setdefault
(
name
,
{})
if
fld
.
type
in
(
'BooleanField'
,
'RadioField'
):
w_args
.
setdefault
(
'class'
,
'form-check-input'
)
else
:
w_args
.
setdefault
(
'class'
,
'form-control'
)
ctn
=
fld
(
**
w_args
)
elif
typ
==
'text'
:
ctn
=
'<span class="input-group-text">
%
s</span>'
%
cnf
[
'text'
]
elif
typ
==
'html'
:
ctn
=
cnf
[
'html'
]
if
ctn
:
if
cnf
[
'pos'
]
==
'prepend'
:
prepend
.
append
(
ctn
)
else
:
append
.
append
(
ctn
)
if
prepend
:
opts
[
'prepend'
]
=
Markup
(
''
.
join
(
prepend
))
if
append
:
opts
[
'append'
]
=
Markup
(
''
.
join
(
append
))
opts
.
update
(
widget_args
.
get
(
self
.
field_name
,
{}))
opts
.
update
(
field_args
)
params
=
{
'form'
:
form
,
'field'
:
field
,
'kwargs'
:
opts
}
return
super
(
Group
,
self
)
.
__call__
(
form
,
form_opts
,
params
)
class
RuleSet
(
object
):
"""
Rule set.
...
...
@@ -406,6 +543,9 @@ class RuleSet(object):
for
r
in
rules
:
if
isinstance
(
r
,
string_types
):
result
.
append
(
self
.
convert_string
(
r
)
.
configure
(
self
,
parent
))
elif
isinstance
(
r
,
(
tuple
,
list
)):
row
=
Row
(
*
r
)
result
.
append
(
row
.
configure
(
self
,
parent
))
else
:
try
:
result
.
append
(
r
.
configure
(
self
,
parent
))
...
...
flask_admin/static/admin/js/bs4_modal.js
View file @
c5f44b10
...
...
@@ -2,3 +2,8 @@
$
(
'body'
).
on
(
'click.modal.data-api'
,
'[data-toggle="modal"]'
,
function
()
{
$
(
$
(
this
).
data
(
"target"
)
+
' .modal-content'
).
load
(
$
(
this
).
attr
(
'href'
));
});
$
(
function
()
{
// Apply flask-admin form styles after the modal is loaded
window
.
faForm
.
applyGlobalStyles
(
document
);
});
flask_admin/static/vendor/select2/select2-bootstrap4.css
View file @
c5f44b10
This diff is collapsed.
Click to expand it.
flask_admin/static/vendor/x-editable/css/bootstrap4-editable.css
0 → 100644
View file @
c5f44b10
This diff is collapsed.
Click to expand it.
flask_admin/static/vendor/x-editable/js/bootstrap4-editable.min.js
0 → 100644
View file @
c5f44b10
This diff is collapsed.
Click to expand it.
flask_admin/templates/bootstrap4/admin/base.html
View file @
c5f44b10
...
...
@@ -78,13 +78,13 @@
{% endblock %}
{% block tail_js %}
<script
src=
"{{ admin_static.url(filename='bootstrap/bootstrap4/js/popper.min.js') }}"
type=
"text/javascript"
></script>
<script
src=
"{{ admin_static.url(filename='vendor/jquery.min.js', v='2.1.4') }}"
type=
"text/javascript"
></script>
<script
src=
"{{ admin_static.url(filename='bootstrap/bootstrap4/js/popper.min.js') }}"
type=
"text/javascript"
></script>
<script
src=
"{{ admin_static.url(filename='bootstrap/bootstrap4/js/bootstrap.min.js', v='4.2.1') }}"
type=
"text/javascript"
></script>
<script
src=
"{{ admin_static.url(filename='vendor/moment.min.js', v='2.9.0') }}"
type=
"text/javascript"
></script>
<script
src=
"{{ admin_static.url(filename='vendor/bootstrap4/dropdown.js', v='4.3.1') }}"
type=
"text/javascript"
></script>
<script
src=
"{{ admin_static.url(filename='vendor/bootstrap4/util.js', v='4.3.1') }}"
type=
"text/javascript"
></script>
<script
src=
"{{ admin_static.url(filename='vendor/bootstrap4/dropdown.js', v='4.3.1') }}"
type=
"text/javascript"
></script>
<script
src=
"{{ admin_static.url(filename='vendor/select2/select2.min.js', v='4.2.1') }}"
type=
"text/javascript"
></script>
{% if admin_view.extra_js %}
...
...
flask_admin/templates/bootstrap4/admin/lib.html
View file @
c5f44b10
...
...
@@ -100,7 +100,7 @@
{# ---------------------- Modal Window ------------------- #}
{% macro add_modal_window(modal_window_id='fa_modal_window', modal_label_id='fa_modal_label') %}
<div
class=
"modal fade"
id=
"{{ modal_window_id }}"
tabindex=
"-1"
role=
"dialog"
aria-labelledby=
"{{ modal_label_id }}"
>
<div
class=
"modal-dialog"
role=
"document"
>
<div
class=
"modal-dialog
modal-xl
"
role=
"document"
>
{# bootstrap version > 3.1.0 required for this to work #}
<div
class=
"modal-content"
>
</div>
...
...
@@ -117,28 +117,46 @@
{# ---------------------- Forms -------------------------- #}
{% macro render_field(form, field, kwargs={}, caller=None) %}
{% set direct_error = h.is_field_error(field.errors) %}
<div
class=
"form-group{{ ' has-error' if direct_error else '' }}"
>
<label
for=
"{{ field.id }}"
class=
"col-md-2 control-label"
>
{{ field.label.text }}
{% set prepend = kwargs.pop('prepend', None) %}
{% set append = kwargs.pop('append', None) %}
<div
class=
"form-group {{ kwargs.get('column_class', '') }}"
>
<label
for=
"{{ field.id }}"
class=
"control-label"
>
{{ field.label.text }}
{% if h.is_required_form_field(field) %}
<strong
style=
"color: red"
>
*
</strong>
{%- else -%}
{%- endif %}
</label>
<div
class=
"{{ kwargs.get('column_class', 'col-md-10') }}"
>
{% set _dummy = kwargs.setdefault('class', 'form-control') %}
{{ field(**kwargs)|safe }}
{% if field.description %}
<p
class=
"help-block"
>
{{ field.description|safe }}
</p>
{% endif %}
{% if prepend or append %}
<div
class=
"input-group"
>
{%- if prepend -%}
<div
class=
"input-group-prepend"
>
{{ prepend }}
</div>
{%- endif -%}
{% endif %}
{% set _class = kwargs.setdefault('class', 'form-control') %}
{%- if direct_error %} {% set _ = kwargs.update({'class': kwargs['class'] ~ ' is-invalid'}) %} {% endif -%}
{{ field(**kwargs) | safe }}
{%- if append -%}
<div
class=
"input-group-append"
>
{{ append }}
</div>
{%- endif -%}
{% if direct_error %}
<ul
class=
"help-block input-errors"
>
{% for e in field.errors if e is string %}
<li>
{{ e }}
</li>
{% endfor %}
</ul>
<div
class=
"invalid-feedback"
>
<ul
class=
"help-block"
>
{% for e in field.errors if e is string %}
<li>
{{ e }}
</li>
{% endfor %}
</ul>
</div>
{% elif field.description %}
<div
class=
"help-block"
>
{{ field.description|safe }}
</div>
{% endif %}
{% if prepend or append %}
</div>
{% endif %}
{% if caller %}
{{ caller(form, field, direct_error, kwargs) }}
{% endif %}
...
...
@@ -178,7 +196,7 @@
{% endmacro %}
{% macro form_tag(form=None, action=None) %}
<form
action=
"{{ action or '' }}"
method=
"POST"
role=
"form"
class=
"admin-form
form-horizontal
"
enctype=
"multipart/form-data"
>
<form
action=
"{{ action or '' }}"
method=
"POST"
role=
"form"
class=
"admin-form"
enctype=
"multipart/form-data"
>
<fieldset>
{{ caller() }}
</fieldset>
...
...
@@ -186,6 +204,15 @@
{% endmacro %}
{% macro render_form_buttons(cancel_url, extra=None, is_modal=False) %}
{% if is_modal %}
<input
type=
"submit"
class=
"btn btn-primary"
value=
"{{ _gettext('Save') }}"
/>
{% if extra %}
{{ extra }}
{% endif %}
{% if cancel_url %}
<a
href=
"{{ cancel_url }}"
class=
"btn btn-danger"
role=
"button"
{%
if
is_modal
%}
data-dismiss=
"modal"
{%
endif
%}
>
{{ _gettext('Cancel') }}
</a>
{% endif %}
{% else %}
<hr>
<div
class=
"form-group"
>
<div
class=
"col-md-offset-2 col-md-10 submit-row"
>
...
...
@@ -198,6 +225,7 @@
{% endif %}
</div>
</div>
{% endif %}
{% endmacro %}
{% macro render_form(form, cancel_url, extra=None, form_opts=None, action=None, is_modal=False) -%}
...
...
flask_admin/templates/bootstrap4/admin/model/list.html
View file @
c5f44b10
...
...
@@ -20,7 +20,7 @@
{% if admin_view.can_create %}
<li
class=
"nav-item"
>
{%- if admin_view.create_modal -%}
{{ lib.add_modal_button(url=get_url('.create_view', url=return_url, modal=True), title=_gettext('Create New Record'), content=_gettext('Create')) }}
{{ lib.add_modal_button(url=get_url('.create_view', url=return_url, modal=True),
btn_class='nav-link',
title=_gettext('Create New Record'), content=_gettext('Create')) }}
{% else %}
<a
href=
"{{ get_url('.create_view', url=return_url) }}"
title=
"{{ _gettext('Create New Record') }}"
class=
"nav-link"
>
{{ _gettext('Create') }}
</a>
{%- endif -%}
...
...
flask_admin/templates/bootstrap4/admin/model/modals/create.html
View file @
c5f44b10
...
...
@@ -5,20 +5,32 @@
{% set render_ctx = h.resolve_ctx() %}
{% block body %}
<div
class=
"modal-header"
>
<button
type=
"button"
class=
"close"
data-dismiss=
"modal"
aria-label=
"Close"
><span
aria-hidden=
"true"
>
×
</span></button>
{% block header_text %}
<h3>
{{ _gettext('Create New Record') }}
</h3>
{% endblock %}
{% block header_text %}
<h5
class=
"modal-title"
>
{{ _gettext('Create New Record') }}
</h5>
{% endblock %}
<button
type=
"button"
class=
"close"
data-dismiss=
"modal"
aria-label=
"Close"
>
<span
aria-hidden=
"true"
>
×
</span>
</button>
</div>
{% call lib.form_tag(action=url_for('.create_view', url=return_url)) %}
<div
class=
"modal-body"
>
{{ lib.render_form_fields(form, form_opts=form_opts) }}
</div>
<div
class=
"modal-footer"
>
{{ lib.render_form_buttons(return_url, extra=None, is_modal=True) }}
</div>
{% endcall %}
{# "save and add" button is removed from modal (it won't function properly) #}
{% block create_form %}
{
#
% block create_form %}
{{ lib.render_form(form, return_url, extra=None, form_opts=form_opts,
action=url_for('.create_view', url=return_url),
is_modal=True) }}
{% endblock %}
</div>
{% endblock % #}
{% endblock %}
{% block tail %}
<script
src=
"{{ admin_static.url(filename='admin/js/bs
3
_modal.js', v='1.0.0') }}"
></script>
<script
src=
"{{ admin_static.url(filename='admin/js/bs
4
_modal.js', v='1.0.0') }}"
></script>
{% endblock %}
flask_admin/templates/bootstrap4/admin/model/modals/edit.html
View file @
c5f44b10
...
...
@@ -6,19 +6,24 @@
{% block body %}
<div
class=
"modal-header"
>
<button
type=
"button"
class=
"close"
data-dismiss=
"modal"
aria-label=
"Close"
><span
aria-hidden=
"true"
>
×
</span></button>
{% block header_text %}
<h3>
{{ _gettext('Edit Record') + ' #' + request.args.get('id') }}
</h3
>
<h5
class=
"modal-title"
>
{{ _gettext('Edit Record') + ' #' + request.args.get('id') }}
</h5
>
{% endblock %}
<button
type=
"button"
class=
"close"
data-dismiss=
"modal"
aria-label=
"Close"
>
<span
aria-hidden=
"true"
>
×
</span>
</button>
</div>
{% call lib.form_tag(action=url_for('.edit_view', id=request.args.get('id'), url=return_url)) %}
<div
class=
"modal-body"
>
{# "save and continue" button is removed from modal (it won't function properly) #}
{% block edit_form %}
{{ lib.render_form(form, return_url, extra=None, form_opts=form_opts,
action=url_for('.edit_view', id=request.args.get('id'), url=return_url),
is_modal=True) }}
{% endblock %}
{{ lib.render_form_fields(form, form_opts=form_opts) }}
</div>
<div
class=
"modal-footer"
>
{{ lib.render_form_buttons(return_url, extra=None, is_modal=True) }}
</div>
{% endcall %}
{% endblock %}
{% block tail %}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment