diff --git a/docs/_data/config-header.yml b/docs/_data/config-header.yml index 0a3b9432..afae02e1 100644 --- a/docs/_data/config-header.yml +++ b/docs/_data/config-header.yml @@ -39,7 +39,9 @@ end: icon: fab fa-github-square title: GitHub url: https://github.com/executablebooks - - type: button - icon: fas fa-comments - title: Forum - url: https://github.com/orgs/executablebooks/discussions + - type: button + icon: fas fa-comments + outline: true + content: Discussion + classes: text-primary + url: https://github.com/orgs/executablebooks/discussions diff --git a/docs/_static/custom.css b/docs/_static/custom.css index f0483400..ac67b7b1 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -1,3 +1,6 @@ :root { - --sd-color-primary: #f37726; + /* WONT WORK UNTIL THE NEXT PYDATA THEME IS RELEASED + --sbt-color-primary: #f37726; + --sd-color-primary: var(--sbt-color-primary); + */ } diff --git a/src/sphinx_book_theme/__init__.py b/src/sphinx_book_theme/__init__.py index cc305967..ae17a4c4 100644 --- a/src/sphinx_book_theme/__init__.py +++ b/src/sphinx_book_theme/__init__.py @@ -76,9 +76,8 @@ def render_component(component): output = COMPONENT_FUNCS[kind](app, context, **component_copy) except Exception as exc: msg = f"Component render failure for:\n{component}\n\n" - msg += f"Exception: {exc}" SPHINX_LOGGER.warn(msg) - return + raise exc return output context["theme_render_component"] = render_component diff --git a/src/sphinx_book_theme/_components.py b/src/sphinx_book_theme/_components.py index b6726917..fb375eaf 100644 --- a/src/sphinx_book_theme/_components.py +++ b/src/sphinx_book_theme/_components.py @@ -16,22 +16,66 @@ def component_button( app, context, - content="", - title="", - icon="", - image="", - url="", - onclick="", - button_id="", - label_for="", - id="", - tooltip_placement="", + content=None, + title=None, + icon=None, + image=None, + outline=None, + id=None, + tooltip_placement=None, + url=None, + onclick=None, + button_id=None, + label_for=None, attributes={}, classes=[], ): + """Render a clickable button. + + There are three possible actions that will be triggered, + corresponding to different kwargs having values. + + Meta Parameters + --------------- + app: An instance of sphinx.Application + context: A Sphinx build context dictionary + + General parameters + ------------------ + content: Content to populate inside the button. + title: A tooltip / accessibility-friendly title. + icon: A tiny square icon. A set of FontAwesome icon classes, or path to an image. + image: A larger image of any aspect ratio. A path to a local or remote image. + button_id: The ID to be added to this button. + outline: Whether to outline the button. + tooltip_placement: Whether the tooltip will be to the left, right, top, or bottom. + attributes: A dictionary of any key:val attributes to add to the button. + classes: A list of CSS classes to add to the button. + + Action-specific parameters + -------------------------- + url: The URL to which a button will direct when clicked. + onclick: JavaScript that will be called when a person clicks. + label_for: The input this label should trigger when clicked (button is a label). + """ + # Set up attributes and classes that will be used to create HTML attributes at end attributes = attributes.copy() - attributes.update({"type": "button", "class": ["btn", "icon-button"]}) - attributes["class"].extend(classes.copy()) + attributes.update({"type": "button"}) + + # Update classes with custom added ones + default_classes = ["btn", "icon-button"] + if classes: + if isinstance(classes, str): + classes = [classes] + else: + classes = [] + classes.extend(default_classes) + + # Give an outline if desired. + if outline: + classes.append("btn-outline") + + # Checks for proper arguments btn_content = "" if url and onclick: raise Exception("Button component cannot have both url and onclick specified.") @@ -62,33 +106,23 @@ def component_button( """ if not content: - attributes["class"].append("icon-button-no-content") + classes.append("icon-button-no-content") else: - btn_content += content + btn_content += f'{content}' if button_id: attributes["id"] = button_id - attributes["aria-label"] = title - - # Handle tooltips - title = context["translate"](title) - tooltip_placement = "bottom" if not tooltip_placement else tooltip_placement - - # If we're already using data-toggle, wrap the button content in a span. - # This lets us use another data-toggle. - if "data-toggle" in attributes: - btn_content = f""" - - {btn_content} - - """ # noqa - else: + # Handle tooltips if a title is given + if title: + title = context["translate"](title) + tooltip_placement = "bottom" if not tooltip_placement else tooltip_placement + attributes["aria-label"] = title attributes["data-placement"] = tooltip_placement attributes["title"] = title # Convert all the options for the button into a string of HTML attributes - attributes["class"] = " ".join(attributes["class"]) + attributes["class"] = " ".join(classes) attributes_str = " ".join([f'{key}="{val}"' for key, val in attributes.items()]) # Generate the button HTML @@ -163,13 +197,17 @@ def component_dropdown( dropdown_id = "menu-dropdown-" dropdown_id += hashlib.md5(dropdown_items.encode("utf-8")).hexdigest()[:5] - # Generate the button HTML + # Generate the dropdown button HTML dropdown_attributes = { "data-toggle": "dropdown", "aria-haspopup": "true", "aria-expanded": "false", "type": "button", } + if "title" in kwargs: + SPHINX_LOGGER.warn("Cannot use title / tooltip with dropdown menu. Removing.") + kwargs.pop("title") + html_button = component_button( app, context, diff --git a/src/sphinx_book_theme/assets/styles/components/_buttons.scss b/src/sphinx_book_theme/assets/styles/components/_buttons.scss index 5f54e592..00bbe507 100644 --- a/src/sphinx_book_theme/assets/styles/components/_buttons.scss +++ b/src/sphinx_book_theme/assets/styles/components/_buttons.scss @@ -13,14 +13,6 @@ } } -// TODO NEW RULES THAT I NEED TO INCORPORATE ABOVE and in other places -// Used when there's a solo icon with no text, so make the icon a bit bigger - -div.dropdown-menu button.btn { - display: flex; - align-items: center; -} - .icon-button { padding: 0.2em 0.2em; margin-bottom: 0; @@ -39,32 +31,73 @@ div.dropdown-menu button.btn { font-size: 1.3rem; } -.dropdown-menu { - // Copied from dropdown menu style above - border-radius: $box-border-radius; - box-shadow: 0px 3px 10px 0px rgba(0, 0, 0, 0.25); +div.dropdown { + // Control the dropdown menu display - button { - padding-left: 0.5rem; - width: 100%; - border-radius: 0; - - &:hover { - background-color: #eee; - } + // First, overwrite the Bootstrap default so we can make nicer animations + // These make it "displayed" but hidden + .dropdown-menu { + display: block; + visibility: hidden; + opacity: 0; + transition: visibility 50ms ease-out, opacity 150ms ease-out 50ms; } - a:hover { - text-decoration: none; + + // On hover, we display the contents via visibility and opacity + &:hover div.dropdown-menu, + div.dropdown-menu:hover { + visibility: visible; + opacity: 1; + // Remove the delay when hovering, which makes it appear instantly, but delay in disappear + transition-delay: 0ms; } - span.btn__icon-container { - display: flex; - margin-right: 0.5rem; + // Other styling on the dropdown menu + .dropdown-menu { + // Copied from dropdown menu style above + border-radius: $box-border-radius; + box-shadow: 0px 3px 10px 0px rgba(0, 0, 0, 0.25); + + button { + display: flex; + align-items: center; + padding-left: 0.5rem; + width: 100%; + border-radius: 0; + + &:hover { + background-color: #eee; + } + } - img, - i { - width: 1.2rem; // Slightly wider to make the font match the images - font-size: 1rem; + a:hover { + text-decoration: none; } + + span.btn__icon-container { + display: flex; + margin-right: 0.5rem; + + img, + i { + width: 1.2rem; // Slightly wider to make the font match the images + font-size: 1rem; + } + } + } +} + +// Extra spacing when we have both icons and content +span.btn__icon-container ~ .btn__content-container { + margin-left: 0.5em; +} + +// Outline buttons need extra spacing +button.btn-outline { + outline: 1px solid $non-content-grey; + padding: 0.3rem 0.5rem; + + &:hover { + background-color: #ededed; } } diff --git a/src/sphinx_book_theme/assets/styles/sections/_header-article.scss b/src/sphinx_book_theme/assets/styles/sections/_header-article.scss index dc763366..055ea700 100644 --- a/src/sphinx_book_theme/assets/styles/sections/_header-article.scss +++ b/src/sphinx_book_theme/assets/styles/sections/_header-article.scss @@ -22,11 +22,16 @@ .header-article__right { display: flex; align-items: center; - gap: 0.5rem; } .header-article__right { margin-left: auto; + + button, + label, + div { + padding-left: 0.25rem; + } } } } diff --git a/src/sphinx_book_theme/header_buttons/__init__.py b/src/sphinx_book_theme/header_buttons/__init__.py index 1a61c005..47131aa7 100644 --- a/src/sphinx_book_theme/header_buttons/__init__.py +++ b/src/sphinx_book_theme/header_buttons/__init__.py @@ -118,7 +118,6 @@ def add_header_buttons(app, pagename, templatename, context, doctree): header_buttons.append( { "type": "dropdown", - "title": "Source repositories", "icon": "fab fa-github", "items": repo_buttons, "side": "right", @@ -171,7 +170,6 @@ def add_header_buttons(app, pagename, templatename, context, doctree): header_buttons.append( { "type": "dropdown", - "title": "Download this page", "icon": "fas fa-download", "items": download_buttons, "side": "right", diff --git a/src/sphinx_book_theme/header_buttons/launch.py b/src/sphinx_book_theme/header_buttons/launch.py index 2a5dfada..678bf13c 100644 --- a/src/sphinx_book_theme/header_buttons/launch.py +++ b/src/sphinx_book_theme/header_buttons/launch.py @@ -180,7 +180,6 @@ def add_launch_buttons( header_buttons.append( { "type": "dropdown", - "title": "Launch interactive content", "icon": "fas fa-rocket", "items": launch_buttons_list, "side": "right",