From 9f63896c5da4d2a151f5dd813c5c9a8d4fa9f457 Mon Sep 17 00:00:00 2001 From: Edo Storm Date: Thu, 19 Sep 2024 15:58:26 +0200 Subject: [PATCH] feat: make stepper bubbles collapse and refine css working --- proposals/utils/stepper.py | 34 ++++++++++++++---- proposals/utils/stepper_helpers.py | 58 ++++++++++++++++++------------ 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/proposals/utils/stepper.py b/proposals/utils/stepper.py index d59c61ca8..1f1e15a59 100644 --- a/proposals/utils/stepper.py +++ b/proposals/utils/stepper.py @@ -36,6 +36,9 @@ def __init__( # which item is current self.request = request self.items = [] + self.current_item = None + self.current_item_parent = None + self.check_all(self.starting_checkers) def get_context_data(self): @@ -84,8 +87,17 @@ def build_stepper( raise RuntimeError( "Base layout was never defined for this stepper", ) - # First, insert all items into the layout + # First, insert all items into the layout & let them figure out their + #own styling for item in self.items: + #Only check item.is_current until there is a current item found + while not self.current_item: + if item.is_current(self.request): + self.current_item = item + self.current_item_ancestor = item.get_ancestor() + else: + break + item.get_css_classes() self._insert_item(layout, item) # Second, replace all remaining empty slots in the layout # by PlaceholderItems @@ -96,12 +108,18 @@ def _insert_item(self, layout, new_item): """ Inserts a stepper item into a layout, in-place. """ - # We're only concerned with top-level items, children can sort - # themselves out - if new_item.parent: - return new_item.parent.children.append( - new_item, - ) + if not self.current_item: + # We're only concerned with top-level items, children can sort + # themselves out + if new_item.parent: + return new_item.parent.children.append( + new_item, + ) + elif new_item.get_ancestor() == self.current_item_ancestor: + if new_item.parent: + return new_item.parent.children.append( + new_item, + ) # Step through the layout looking for empty slots, which are # represented by tuples of locations and titles for index, slot in enumerate(layout): @@ -120,6 +138,8 @@ def _insert_placeholders(self, layout): for index, slot in enumerate(layout): # Skip slots that are already items if isinstance(slot, StepperItem): + if slot != self.current_item_ancestor: + slot.children = [] continue # Remaining empty slots are replaced by placeholders placeholder = PlaceholderItem( diff --git a/proposals/utils/stepper_helpers.py b/proposals/utils/stepper_helpers.py index 539348a49..16bf9ff43 100644 --- a/proposals/utils/stepper_helpers.py +++ b/proposals/utils/stepper_helpers.py @@ -50,6 +50,7 @@ def __init__(self, stepper, parent=None, title=None, location=None): self.children = [] self.parent = parent self.available = False + self.css_classes = "" # Don't override default location if not provided explicitly if location: self.location = location @@ -70,23 +71,34 @@ def get_url(self): def get_errors(self): return [] - - def css_classes( - self, - ): - classes = [] - if self.is_current(self.stepper.request): - classes.append( - "active", - ) - return " ".join(classes) + + def get_ancestor(self): + """ + Returns the top level parent, or ancestor + """ + ancestor = self + while ancestor.parent: + ancestor = ancestor.parent + return ancestor + + def get_family(self): + """ + Returns all items with the same ancestor + """ + family = [] + if not self.parent: + return family + def is_current(self, request): """ Returns True if this item represents the page the user - is currently on. + is currently on and appends the active class to the item's + css_classes attribute. This gets called when building the stepper, + until is_current returns True. """ if request.path == self.get_url(): + self.css_classes += "active " return True return False @@ -94,6 +106,13 @@ def is_disabled(self): if not self.get_url(): return True return False + + def get_css_classes(self): + """ + A method to be overwritten by child classes to append to an item's + self.css_classes attribute. This gets called when building the stepper. + """ + pass class PlaceholderItem(StepperItem): @@ -129,16 +148,14 @@ def is_current(self, request): """ return False - def css_classes(self): - css_classes = super().css_classes() + def get_css_classes(self): child_errors = [] for child in self.children: child_errors += child.get_errors() if child_errors != []: - css_classes += "incomplete" + self.css_classes += "incomplete" else: - css_classes += " complete" - return css_classes + self.css_classes += " complete" class ModelFormChecker( @@ -261,14 +278,11 @@ def get_errors(self): return self.get_checker_errors() or self.model_form.errors return self.model_form.errors - def css_classes(self): - css_classes = super().css_classes() - + def get_css_classes(self): if self.get_errors(): - css_classes += " incomplete" + self.css_classes += " incomplete" else: - css_classes += " complete" - return css_classes + self.css_classes += " complete" class UpdateOrCreateChecker(