Source code for sphinx_hosting.wildewidgets.classifier

from django.utils.text import slugify
from wildewidgets import (
    Block,
    CardWidget,
    CheckboxInputBlock,
    CollapseWidget,
    HorizontalLayoutBlock,
    LinkButton,
    NavLinkToggle,
    UnorderedList,
)

from ..models import Classifier, ClassifierNode


[docs]class ClassifierFilterForm(Block): """ The tree-like classifier filter form that appears to the right of the :py:class:`sphinx_hosting.wildewidgets.project.ProjectTable`. It is embedded in :py:class:`ClassifierFilterBlock`. It allows the user to select a set of classifiers by which to filter the projects listing table. """ SCRIPT: str = """ $(document).ready(function() {{ function classifier_filter_check_parents($li, state) {{ var $siblings = $li.siblings(); var $parent = $li.parent().closest('li'); state = state && $siblings.children('label').find('input').prop('checked'); $parent.children('label').find('input').prop('checked', state); if ($parent.parents('li').length) {{ classifier_filter_check_parents($parent, state); }}; }} function classifier_filter_open_sections() {{ $('{block_id} input[type="checkbox"]:checked').each(function() {{ var last = null; var target = null; var parents = $(this).parents().each(function() {{ if ($(this).hasClass('classifiers__filter__form')) {{ target = last.prev(); return false; }} last = $(this); }}); target.attr('aria-expanded', 'true'); var collapse_id = target.attr('data-bs-target'); $(collapse_id).addClass('show'); }}); }} classifier_filter_open_sections(); $('{block_id} input').change(function () {{ var $cb = $(this); var $li = $cb.closest('li'); var state = $cb.prop('checked'); // check all children $li.find('input').prop('checked', state); // check all parents, as applicable if ($li.parents('li').length) {{ classifier_filter_check_parents($li, state); }}; }}); $('{block_id} .classifier--submit').on('click', function () {{ var classifier_ids = []; $('{block_id} input[type="checkbox"]:checked').each(function() {{ classifier_ids.push($(this).val()); }}); var table = $('.{table_name}').DataTable(); table.column({column_number}).search(classifier_ids.join()).draw(); }}); $('{block_id} .classifier--clear').on('click', function () {{ $('{block_id} input[type="checkbox"]:checked').each(function() {{ $(this).prop('checked', false) }}); }}); }}); """ block: str = "classifiers__filter__form" tag: str = "form"
[docs] def __init__(self, table_name: str, column_number: int, **kwargs): self.table_name = table_name self.column_number = column_number super().__init__(**kwargs) self._css_id = self.block self.script = self.SCRIPT.format( block_id=f"#{self._css_id}", column_number=self.column_number, table_name=self.table_name, ) self._attributes["method"] = "get" self._attributes["name"] = self.block self.tree: dict[str, ClassifierNode] = Classifier.objects.tree() for node in self.tree.values(): name = slugify(node.title) target = f"#{name}" self.add_block( NavLinkToggle(node.title, collapse_id=target, css_class="my-2") ) contents = UnorderedList() self.add_block(CollapseWidget(contents, css_id=name)) self.add_subtree(contents, node.items) self.add_block( HorizontalLayoutBlock( LinkButton( name="classifier--submit", text="Filter Projects", color="primary", url="#", ), LinkButton( name="classifier--clear", text="Clear", color="outline-secondary", css_class="mt-2 mt-xl-0", url="#", ), css_class="my-3", justify="between", flex_size="xl", ) )
[docs] def add_subtree( self, contents: UnorderedList, nodes: dict[str, ClassifierNode] ) -> None: """ Add a subtree of classifier checkboxes. Args: contents: the ``<ul>`` block to which to add our list of classifier checkboxes nodes (_type_): _description_ nodes: the classifier nodes to add to the list. The key is the classifier id, and the value is the :py:class:`ClassifierNode` instance. """ for node in nodes.values(): checkbox = self.get_checkbox(node) if node.items: container = Block(tag="li") container.add_block(checkbox) sub_contents = UnorderedList() container.add_block(sub_contents) contents.add_block(container) self.add_subtree(sub_contents, node.items) else: contents.add_block(checkbox)
[docs] def get_checkbox(self, node: ClassifierNode) -> HorizontalLayoutBlock: """ Build and return the :py:class:`wildewidgets.CheckboxInputBlock` for the classifier ``node``. Args: node: the classifier data Returns: A configured :py:class:`wildewidgets.CheckboxInputBlock` """ value = node.classifier.id if node.classifier else "empty" return CheckboxInputBlock( label_text=node.title, bold=False, input_name=slugify(node.title), value=value, )
[docs]class ClassifierFilterBlock(CardWidget): """ A :py:class:`wildewidgets.CardWidget` that contains the :py:class:`ClassifierFilterForm`. This the right of the :py:class:`sphinx_hosting.wildewidgets.project.ProjectTable`. This widget is embedded in :py:class:`sphinx_hosting.wildewdigets.project.ProjectTableWidget` Args: table_name: the name of the dataTables table to control column_number: the number of the column in the dataTable that contains classifier names """ name: str = "classifiers__filter" card_title: str = "Filter by classifier"
[docs] def __init__(self, table_name: str, column_number: int, **kwargs): self.table_name = table_name self.column_number = column_number super().__init__(**kwargs) self.set_widget( ClassifierFilterForm( table_name=self.table_name, column_number=column_number ) )