Source code for pybloqs.block.layout

import copy
import math
from typing import List

from pybloqs.block.base import BaseBlock
from pybloqs.block.convenience import Block, add_block_types
from pybloqs.html import append_to
from pybloqs.util import Cfg


class CompositeBlockMixin:
    """
    Mixin to support composite blocks. Must have a `_contents` attribute!
    """

    def _visit(self, visitor) -> "CompositeBlockMixin":
        """
        Applies a visitor to child items of this container. In case
        the visitor returns a new object, a new container containing the new item(s)
        is returned. In case the visitor returns None, the item is excluded from the
        new container.

        :param visitor: Visitor function
        :return: In case the visitor made changes, a new container. Otherwise the
                 current instance is returned.
        """
        contents = []
        transformed = False

        # Loop through the contents
        for item in self._contents:
            result = item._visit(visitor)

            # Check if the visitor returned something new
            if result != item:
                transformed = True

            if result is not None:
                contents.append(result)

        # If any transformation was done, return a new container
        if transformed:
            # Make a shallow copy
            other = copy.copy(self)
            # Replace the contents
            other._contents = contents
            return other
        else:
            return self

    @staticmethod
    def _blockify_contents(contents, kwargs, parent_title_level) -> List[BaseBlock]:
        """
        Blockify the contents
        """
        return [Block(content) for content in contents]


[docs] class Flow(CompositeBlockMixin, BaseBlock): def __init__(self, contents, cascade_cfg: bool = True, **kwargs) -> None: """ Create a block that lays out its contents in a free-flow, element-after-element fashion. :param contents: A list, tuple or set of elements. :param cascade_cfg: Set to True to enable parmater cascading from this block. A value of False means that child blocks do not inherit parameters from this block. :param kwargs: Optional styling arguments. The `style` keyword argument has special meaning in that it allows styling to be grouped as one argument. It is also useful in case a styling parameter name clashes with a standard block parameter. """ super().__init__(**kwargs) self._contents = self._blockify_contents(contents, kwargs, self._settings.title_level) self._cascade_cfg = cascade_cfg def _write_contents(self, container, actual_cfg, *args, **kwargs) -> None: for content in self._contents: content._write_block(container, actual_cfg if self._cascade_cfg else Cfg(), *args, **kwargs)
[docs] class VStack(CompositeBlockMixin, BaseBlock): def __init__(self, contents, cascade_cfg: bool = True, **kwargs) -> None: """ Create vertical stack layout. :param contents: A list, tuple or set of elements. :param cascade_cfg: Set to True to enable parmater cascading from this block. A value of False means that child blocks do not inherit parameters from this block. :param kwargs: Optional styling arguments. The `style` keyword argument has special meaning in that it allows styling to be grouped as one argument. It is also useful in case a styling parameter name clashes with a standard block parameter. """ super().__init__(**kwargs) self._contents = self._blockify_contents(contents, kwargs, self._settings.title_level) self._cascade_cfg = cascade_cfg def _write_contents(self, container, actual_cfg, *args, **kwargs) -> None: for content in self._contents: cell = append_to(container, "div") content._write_block(cell, actual_cfg if self._cascade_cfg else Cfg(), *args, **kwargs)
[docs] class Grid(CompositeBlockMixin, BaseBlock): def __init__(self, contents, cols: int = 1, cascade_cfg: bool = True, **kwargs) -> None: """ Create a block that lays out its contents in a grid. :param contents: A list, tuple or set of elements. :param cols: Desired number of columns in the grid. :param cascade_cfg: Set to True to enable parmater cascading from this block. A value of False means that child blocks do not inherit parameters from this block. :param kwargs: Optional styling arguments. The `style` keyword argument has special meaning in that it allows styling to be grouped as one argument. It is also useful in case a styling parameter name clashes with a standard block parameter. """ super().__init__(**kwargs) self._contents = self._blockify_contents(contents, kwargs, self._settings.title_level) self._cols = cols self._cascade_cfg = cascade_cfg def _write_contents(self, container, actual_cfg, *args, **kwargs) -> None: # The width of one column in percentage content_count = len(self._contents) # Skip layout if there is no content. if content_count > 0: cell_width = 100.0 / min(self._cols, content_count) row_count = int(math.ceil(content_count / float(self._cols))) for row_i in range(row_count): row_el = append_to(container, "div") row_el["class"] = ["pybloqs-grid-row"] if row_i > 0: row_el["style"] = "clear:both" written_row_item_count = row_i * self._cols for col_i in range(self._cols): item_count = written_row_item_count + col_i if item_count >= content_count: break cell_el = append_to(row_el, "div", style=f"width:{cell_width:f}%;float:left;") cell_el["class"] = ["pybloqs-grid-cell"] self._contents[item_count]._write_block( cell_el, actual_cfg if self._cascade_cfg else Cfg(), *args, **kwargs ) # Clear the floating, Yarr! append_to(container, "div", style="clear:both")
[docs] class HStack(Grid): def __init__(self, contents, cascade_cfg: bool = True, **kwargs) -> None: """ Create a horizontal stack layout that puts contents side by side. :param contents: A list, tuple or set of elements. :param cols: Desired number of columns in the grid. :param cascade_cfg: Set to True to enable parmater cascading from this block. A value of False means that child blocks do not inherit parameters from this block. :param kwargs: Optional styling arguments. The `style` keyword argument has special meaning in that it allows styling to be grouped as one argument. It is also useful in case a styling parameter name clashes with a standard block parameter. """ super().__init__(contents, cascade_cfg=cascade_cfg, **kwargs) # Set the column count here because the contents can be processed by mixins/superclasses self._cols = len(self._contents)
add_block_types((list, tuple, set), Grid)