import os.path
import sass
from collections import OrderedDict
from itertools import chain
from io import StringIO
from onegov.core.theme import Theme as CoreTheme
from typing import Any, TYPE_CHECKING
if TYPE_CHECKING:
from collections.abc import Iterator, Mapping, Sequence
[docs]
class BaseTheme(CoreTheme):
""" Base class for Zurb Foundation based themes. Use this class to
create a theme that customizes Zurb Foundation somehow.
If you don't want to customize it at all, use :class:`Theme`.
To customize start like this::
from onegov.foundation import BaseTheme
class MyTheme(BaseTheme):
name = 'my-theme'
version = '1.0'
You can then add paths with your own scss files, as well as imports that
should be added *before* the foundation theme, and imports that should
be added *after* the foundation theme.
Finally, options passed to the :meth:`compile` function take this form::
options = {
'rowWidth': '1000px',
'columnGutter': '30px'
}
Those options result in variables added at the very top of the sass source
before it is compiled::
@import 'foundation/functions';
$rowWidth: 1000px;
$columnGutter: 30px;
If your variables rely on a certain order you need to pass an ordered dict.
"""
def __init__(self, compress: bool = True):
""" Initializes the theme.
:compress:
If true, which is the default, the css is compressed before it is
returned.
"""
[docs]
self.compress = compress
@property
[docs]
def default_options(self) -> dict[str, Any]:
""" Default options used when compiling the theme. """
# return an ordered dict, in case someone overrides the compile options
# with an ordered dict - this would otherwise result in an unordered
# dict when both dicts are merged
return OrderedDict()
@property
[docs]
def pre_imports(self) -> list[str]:
""" Imports added before the foundation import. The imports must be
found in one of the paths (see :attr:`extra_search_paths`).
The form of a single import is 'example' (which would search for
files named 'example.scss')
"""
return []
@property
[docs]
def foundation_components(self) -> 'Sequence[str]':
""" All used foundation components. """
return (
'grid',
'accordion',
'alert-boxes',
'block-grid',
'breadcrumbs',
'button-groups',
'buttons',
'clearing',
'dropdown',
'dropdown-buttons',
'flex-video',
'forms',
'icon-bar',
'inline-lists',
'joyride',
'keystrokes',
'labels',
'magellan',
'orbit',
'pagination',
'panels',
'pricing-tables',
'progress-bars',
'range-slider',
'reveal',
'side-nav',
'split-buttons',
'sub-nav',
'switches',
'tables',
'tabs',
'thumbs',
'tooltips',
'top-bar',
'type',
'offcanvas',
'visibility',
)
@property
[docs]
def imports(self) -> 'Iterator[str]':
""" All imports, including the foundation ones. Override with care. """
return chain(
self.pre_imports,
('normalize', ),
(
'foundation/components/{}'.format(component)
for component in self.foundation_components
),
('fixes', ),
self.post_imports
)
@property
[docs]
def post_imports(self) -> list[str]:
"""
Imports added after the foundation import. The imports must be found
in one of the paths (see :attr:`extra_search_paths`).
The form of a single import is 'example' (which would search for
files named 'example.scss')
"""
return []
@property
@property
[docs]
def foundation_path(self) -> str:
""" The search path for the foundation files included in this module.
"""
return os.path.join(os.path.dirname(__file__), 'foundation')
[docs]
def compile(self, options: 'Mapping[str, Any] | None' = None) -> str:
""" Compiles the theme with the given options. """
# copy, because the dict may be static if it's a basic property
_options = self.default_options.copy()
_options.update(options or {})
theme = StringIO()
print("@import 'foundation/functions';", file=theme)
for key, value in _options.items():
print('${}: {};'.format(key, value), file=theme)
for i in self.imports:
print("@import '{}';".format(i), file=theme)
paths = self.extra_search_paths
paths.append(self.foundation_path)
return sass.compile(
string=theme.getvalue(),
include_paths=paths,
output_style='compressed' if self.compress else 'nested'
)
[docs]
class Theme(BaseTheme):
""" Zurb Foundation vanilla theme. Use this if you don't want any changes
to zurb foundation, except for setting supported variables.
Do not use this class as a base for your own theme!
Example::
from onegov.core import Framework
from onegov.foundation import Theme
class App(Framework):
theme_options = {
'rowWidth': '1200px'
}
@App.setting(section='core', name='theme')
def get_theme():
return Theme()
"""
[docs]
name = 'zurb.foundation'