Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bootstrap v4 initial support #164

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
942a432
Updated Bootstrap to v4.0.0-alpha.6. Added Tether,js as required by B…
jeevcat Jun 30, 2017
5d7357c
Update version in all files
beenje Aug 3, 2017
418e6a0
Remove html5shiv.js and respond.js
beenje Aug 3, 2017
70727df
Update template meta tags
beenje Aug 3, 2017
f3b487e
Update bootstrap CDN
beenje Aug 3, 2017
82b2af6
Bump jquery version to 3.2.1
beenje Aug 3, 2017
40f2bbd
Clean bootstrap css
beenje Aug 3, 2017
6b49849
Remove Glyphicons icon fonts and icon macro
beenje Aug 3, 2017
fe0e4c4
Improve bootstrap version regular expression
beenje Aug 5, 2017
e44689c
Add success message category
beenje Aug 5, 2017
db5316f
Update navbar for bootstrap v4
beenje Aug 3, 2017
c7e56bb
Update forms for bootstrap v4
beenje Aug 6, 2017
0550bfa
Update sample applications for bootstrap v4
beenje Aug 6, 2017
47c1029
Update to bootstrap v4.0.0-beta
beenje Aug 12, 2017
a4534b5
Bump version to 4.0.0-beta.0.dev1
beenje Aug 12, 2017
f674bef
Update bootstrap version regex for beta
beenje Aug 12, 2017
f929537
Replace tether.js with popper.js for v4 beta
beenje Aug 12, 2017
1be0423
Update navbar for v4.0.0-beta
beenje Aug 12, 2017
2cc2924
Fix default button in v4.0.0-beta
beenje Sep 8, 2017
c220aa8
Replace form-control-feedback with invalid-feedback
beenje Nov 28, 2017
89cfcda
Update to bootstrap v4.0.0-beta.3
beenje Dec 29, 2017
52cff6f
Bump popper.js to 1.12.9
beenje Dec 29, 2017
7db4b8c
Bump version to 4.0.0-beta.3.dev1
beenje Dec 29, 2017
82ddbd5
Update to bootstrap v4.0.0
beenje Jan 18, 2018
7a80a03
Bump version to 4.0.0.0.dev1
beenje Jan 18, 2018
d2905d1
get validation to work for bootstrap 4
rakelkar May 28, 2018
79b37d9
Merge pull request #1 from rakelkar/bootstrap-v4
beenje Jun 9, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

project = u'Flask-Bootstrap'
copyright = u'2013, Marc Brinkmann'
version = '3.3.7.2'
release = '3.3.7.2.dev1'
version = '4.0.0.0.dev1'
release = '4.0.0.0'

extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'alabaster']
source_suffix = '.rst'
Expand Down
22 changes: 7 additions & 15 deletions docs/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -167,25 +167,17 @@ the styles block inside your template derived base template::

.. _jquery-faq:

Why are you shipping jQuery 1 instead of jQuery 2?
Why are you shipping jQuery 3 instead of jQuery 1?
--------------------------------------------------

As of this writing (July 2014), there are two key differences between jQuery 1
and 2: Version 1 supports IE6-8 while version 2 drops the support for these old
versions in exchange for a smaller memory footprint and a few performance
gains. At least 20% of the browser landscape (source: `NetMarketShare
<http://www.netmarketshare.com /browser- market-
share.aspx?qprid=2&qpcustomd=0>`_) still consists of browsers not supported by
jQuery 2.
Bootstrap v4 dropped IE8 and IE9 support.
The only advantage of jQuery 1 was the older browser support (IE6-8).
`jQuery 3.0 <https://blog.jquery.com/2016/06/09/jquery-3-0-final-released/>`_ is the future of jQuery.

Unless you have specialized needs, the advantages of jQuery 2 still
do not outweigh the disadvantages of not supporting a fifth of the market. In
the end, Bootstrap and jQuery both aim at abstracting away difficult to handle
quirks when building sites and this goal is currently better served with the
wide support of jQuery1.
If you need IE8-9 support, you should use Bootstrap v3 and jQuery 1.12.


How can I use jQuery2 instead of jQuery1?
How can I use jQuery1 instead of jQuery3?
-----------------------------------------

.. highlight:: python
Expand All @@ -195,7 +187,7 @@ from a different source::

from flask_bootstrap import WebCDN
app.extensions['bootstrap']['cdns']['jquery'] = WebCDN(
'//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/'
'//cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/'
)

This will load ``jquery.js`` or whatever is required from the WebCDN specified.
Expand Down
31 changes: 4 additions & 27 deletions docs/macros.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,11 @@ In addition to the small macros on this page, broad support for other libraries
is also available; see :doc:`forms` and :doc:`sqlalchemy` for details.


Fixes
-----

Cross-browser fixes (specifically for Internet Explorer < 9) are usually
included, but not shipped with Flask-Bootstrap. You can download `html5shiv
<https://raw.github.com/aFarkas/html5shiv/master/dist/html5shiv.min.js>`_ and
`Respond.js <https://raw.githubusercontent.com/scottjehl/Respond/master/dest/
respond.min.js>`_, put them in your applications static folder and include them
like in this example::

{% import "bootstrap/fixes.html" as fixes %}
{% block head %}
{{super()}}
{{fixes.ie8()}}
{% endblock %}
Browser support
---------------

While the scripts are not included, links to them on CDNs are, so if you do not
use ``BOOTSTRAP_SERVE_LOCAL``, they will work out of the box. See :doc:`cdn`
for more details on how CDN-delivery works with Flask-Bootstrap.
Bootstrap v4 dropped IE8, IE9, and iOS 6 support.
v4 is now only IE10+ and iOS 7+. For sites needing either of those, use v3.


Google Analytics
Expand Down Expand Up @@ -134,15 +120,6 @@ preferred way to utilize HTML inside messages now is by using the
+ Markup('</i>!'),
'danger')

.. py:function:: icon(type, extra_classes, **kwargs)

Renders a Glyphicon in a ``<span>`` element.

:param messages: The short name for the icon, e.g. ``remove``.
:param extra_classes: A list of additional classes to add to the class
attribute.
:param kwargs: Additional html attributes.


.. py:function:: form_button(url, content, method='post', class='btn-link',\
**kwargs)
Expand Down
33 changes: 17 additions & 16 deletions flask_bootstrap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@ def is_hidden_field_filter(field):

from .forms import render_form

__version__ = '3.3.7.1.dev1'
BOOTSTRAP_VERSION = re.sub(r'^(\d+\.\d+\.\d+).*', r'\1', __version__)
JQUERY_VERSION = '1.12.4'
HTML5SHIV_VERSION = '3.7.3'
RESPONDJS_VERSION = '1.4.2'
__version__ = '4.0.0.0.dev1'
BOOTSTRAP_VERSION_RE = re.compile(r'(\d+\.\d+\.\d+(\-[a-z]+)?)')
POPPER_VERSION = '1.12.9'
JQUERY_VERSION = '3.2.1'


def get_bootstrap_version(version):
return BOOTSTRAP_VERSION_RE.match(version).group(1)


BOOTSTRAP_VERSION = get_bootstrap_version(__version__)


class CDN(object):
Expand Down Expand Up @@ -162,30 +168,25 @@ def init_app(self, app):
def lwrap(cdn, primary=static):
return ConditionalCDN('BOOTSTRAP_SERVE_LOCAL', primary, cdn)

popper = lwrap(
WebCDN('//cdnjs.cloudflare.com/ajax/libs/popper.js/%s/' %
POPPER_VERSION), local)

bootstrap = lwrap(
WebCDN('//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/%s/' %
WebCDN('//maxcdn.bootstrapcdn.com/bootstrap/%s/' %
BOOTSTRAP_VERSION), local)

jquery = lwrap(
WebCDN('//cdnjs.cloudflare.com/ajax/libs/jquery/%s/' %
JQUERY_VERSION), local)

html5shiv = lwrap(
WebCDN('//cdnjs.cloudflare.com/ajax/libs/html5shiv/%s/' %
HTML5SHIV_VERSION))

respondjs = lwrap(
WebCDN('//cdnjs.cloudflare.com/ajax/libs/respond.js/%s/' %
RESPONDJS_VERSION))

app.extensions['bootstrap'] = {
'cdns': {
'local': local,
'static': static,
'popper': popper,
'bootstrap': bootstrap,
'jquery': jquery,
'html5shiv': html5shiv,
'respond.js': respondjs,
},
}

Expand Down
10 changes: 5 additions & 5 deletions flask_bootstrap/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ def _wrapped_input(self, node,
return wrap

def visit_BooleanField(self, node):
wrap = self._get_wrap(node, classes='checkbox')
wrap = self._get_wrap(node, classes='form-check')

label = wrap.add(tags.label(_for=node.id))
label.add(tags.input(type='checkbox'))
label = wrap.add(tags.label(_for=node.id, _class='form-check-label'))
label.add(tags.input(type='checkbox', _class='form-check-input'))
label.add(node.label.text)

return wrap
Expand Down Expand Up @@ -86,7 +86,7 @@ def visit_Field(self, node):
wrap.add(raw(node()))

if node.description:
wrap.add(tags.p(node.description, _class='help-block'))
wrap.add(tags.p(node.description, _class='form-text text-muted'))

return wrap

Expand Down Expand Up @@ -136,7 +136,7 @@ def visit_PasswordField(self, node):

def visit_SubmitField(self, node):
button = tags.button(node.label.text,
_class='btn btn-default',
_class='btn btn-secondary',
type='submit')
return button

Expand Down
72 changes: 34 additions & 38 deletions flask_bootstrap/nav.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,34 @@ def visit_Navbar(self, node):
node_id = self.id or sha1(str(id(node)).encode()).hexdigest()

root = tags.nav() if self.html5 else tags.div(role='navigation')
root['class'] = 'navbar navbar-default'
root['class'] = 'navbar navbar-expand-lg navbar-light bg-light'

cont = root.add(tags.div(_class='container-fluid'))
# title may also have a 'get_url()' method, in which case we render
# a brand-link
if node.title is not None:
if hasattr(node.title, 'get_url'):
root.add(tags.a(node.title.text, _class='navbar-brand',
href=node.title.get_url()))
else:
root.add(tags.span(node.title, _class='navbar-brand'))

# collapse button
header = cont.add(tags.div(_class='navbar-header'))
btn = header.add(tags.button())
btn = root.add(tags.button())
btn['type'] = 'button'
btn['class'] = 'navbar-toggle collapsed'
btn['class'] = 'navbar-toggler navbar-toggler-right'
btn['data-toggle'] = 'collapse'
btn['data-target'] = '#' + node_id
btn['aria-expanded'] = 'false'
btn['aria-controls'] = 'navbar'
btn['aria-label'] = 'Toggle navigation'

btn.add(tags.span('Toggle navigation', _class='sr-only'))
btn.add(tags.span(_class='icon-bar'))
btn.add(tags.span(_class='icon-bar'))
btn.add(tags.span(_class='icon-bar'))
btn.add(tags.span(_class='navbar-toggler-icon'))

# title may also have a 'get_url()' method, in which case we render
# a brand-link
if node.title is not None:
if hasattr(node.title, 'get_url'):
header.add(tags.a(node.title.text, _class='navbar-brand',
href=node.title.get_url()))
else:
header.add(tags.span(node.title, _class='navbar-brand'))

bar = cont.add(tags.div(
bar = root.add(tags.div(
_class='navbar-collapse collapse',
id=node_id,
))
bar_list = bar.add(tags.ul(_class='nav navbar-nav'))
bar_list = bar.add(tags.div(_class='navbar-nav'))

for item in node.items:
bar_list.add(self.visit(item))
Expand All @@ -56,47 +51,48 @@ def visit_Navbar(self, node):

def visit_Text(self, node):
if not self._in_dropdown:
return tags.p(node.text, _class='navbar-text')
return tags.li(node.text, _class='dropdown-header')
return tags.span(node.text, _class='navbar-text')
return tags.h6(node.text, _class='dropdown-header')

def visit_Link(self, node):
item = tags.li()
item.add(tags.a(node.text, href=node.get_url()))

return item
if self._in_dropdown:
return tags.a(node.text, href=node.get_url(),
_class='dropdown-item')
else:
return tags.a(node.text, href=node.get_url(),
_class='nav-item nav-link')

def visit_Separator(self, node):
if not self._in_dropdown:
raise RuntimeError('Cannot render separator outside Subgroup.')
return tags.li(role='separator', _class='divider')
return tags.div(_class='dropdown-divider')

def visit_Subgroup(self, node):
if not self._in_dropdown:
li = tags.li(_class='dropdown')
div = tags.div(_class='nav-item dropdown')
if node.active:
li['class'] = 'active'
a = li.add(tags.a(node.title, href='#', _class='dropdown-toggle'))
div['class'] += ' active'
a = div.add(tags.a(node.title, href='#',
_class='nav-link dropdown-toggle'))
a['data-toggle'] = 'dropdown'
a['role'] = 'button'
a['aria-haspopup'] = 'true'
a['aria-expanded'] = 'false'
a.add(tags.span(_class='caret'))

ul = li.add(tags.ul(_class='dropdown-menu'))
menu = div.add(tags.div(_class='dropdown-menu'))

self._in_dropdown = True
for item in node.items:
ul.add(self.visit(item))
menu.add(self.visit(item))
self._in_dropdown = False

return li
return div
else:
raise RuntimeError('Cannot render nested Subgroups')

def visit_View(self, node):
item = tags.li()
item.add(tags.a(node.text, href=node.get_url(), title=node.text))
item = tags.a(node.text, href=node.get_url(), title=node.text,
_class='nav-item nav-link')
if node.active:
item['class'] = 'active'

item['class'] += ' active'
return item
Loading