Closed pradyunsg closed 3 years ago
Yeh it would be great if we could get this to work. Note, I had a very brief play around with it, but didn't find an immediate solution.
Coolieo. I'll give this a shot!
BTW, still waiting for your reply on https://github.com/executablebooks/meta/issues/137 😉. It would be great to get that worked out before doing any further work here and sphinx-book-theme etc.
Wait, what?
Oh, I thought I wrote a big comment there alreadY!
Question: would it be OK to inject a script tag in the document for this? The following gets 80% there, but it'd need us to inject a style tag with #<id>:checked + * label[for=<id>] + .tabbed-content {display: block}
for each of the IDs on the page.
diff --git a/sphinx_panels/scss/panels/_tabs.scss b/sphinx_panels/scss/panels/_tabs.scss
index e5156de..7f02029 100644
--- a/sphinx_panels/scss/panels/_tabs.scss
+++ b/sphinx_panels/scss/panels/_tabs.scss
@@ -55,6 +55,34 @@
}
}
+// Hide radio buttons
+.tabbed-input--hidden {
+ opacity: 0;
+ position: absolute;
+
+ // Active tab label
+ &:checked + label {
+ border-color: var(--tabs-color-label-active);
+ color: var(--tabs-color-label-active);
+
+ // Show tabbed block content
+ +.tabbed-content {
+ display: block;
+ }
+ }
+
+ // Focused tab label
+ &:focus + label {
+ outline-style: auto;
+ }
+
+ // Disable focus indicator for pointer devices
+ &:not(.focus-visible) + label {
+ outline: none;
+ -webkit-tap-highlight-color: transparent;
+ }
+}
+
// Tabbed block container
.tabbed-set {
border-radius: px2rem(2px);
@@ -63,40 +91,12 @@
margin: 1em 0;
position: relative;
- // Hide radio buttons
- >input {
- opacity: 0;
- position: absolute;
-
- // Active tab label
- &:checked+label {
- border-color: var(--tabs-color-label-active);
- color: var(--tabs-color-label-active);
-
- // Show tabbed block content
- +.tabbed-content {
- display: block;
- }
- }
-
- // Focused tab label
- &:focus+label {
- outline-style: auto;
- }
-
- // Disable focus indicator for pointer devices
- &:not(.focus-visible)+label {
- outline: none;
- -webkit-tap-highlight-color: transparent;
- }
- }
-
// Tab label
>label {
border-bottom: px2rem(2px) solid transparent;
color: var(--tabs-color-label-inactive);
cursor: pointer;
- font-size: px2rem(20px);
+ font-size: px2rem(14px);
font-weight: 700;
padding: px2em(20px, 20px) 1.25em px2em(10px, 20px);
transition: color 250ms;
diff --git a/sphinx_panels/tabs.py b/sphinx_panels/tabs.py
index ae274a1..405beaa 100644
--- a/sphinx_panels/tabs.py
+++ b/sphinx_panels/tabs.py
@@ -8,6 +8,8 @@ from sphinx.util.docutils import SphinxDirective
from sphinx.util.logging import getLogger
from sphinx.util.nodes import NodeMatcher
+from .utils import slugify
+
LOGGER = getLogger(__name__)
@@ -121,9 +123,29 @@ class TabbedHtmlTransform(SphinxPostTransform):
default_priority = 200
builders = ("html", "dirhtml", "singlehtml", "readthedocs")
+ def __init__(self, document, startnode=None):
+ super().__init__(document, startnode)
+
+ # This is used to ensure we put one <input> element for each title.
+ self._seen_inputs_map = {}
+
+ def add_input_unless_repeated_in_document(self, input_node):
+ node_key = input_node["id"]
+
+ if node_key in self._seen_inputs_map:
+ return True
+
+ # Was not already on input, we'll add it now.
+ self._seen_inputs_map[node_key] = input_node
+ return False
+
def get_unique_key(self):
return str(uuid4())
+ def get_key_for(self, title_tag):
+ title = title_tag.children[0]
+ return "tabbed--extension--" + slugify(title)
+
def run(self):
matcher = NodeMatcher(nodes.container, type="tabbed")
tab_set = None
@@ -140,6 +162,14 @@ class TabbedHtmlTransform(SphinxPostTransform):
tab_set = TabSet(node)
self.render_tab_set(tab_set)
+ # Add inputs that we need to have in the document
+ for input_tag in self._seen_inputs_map.values():
+ self.document.insert(0, input_tag)
+
+ # def add_input_unless_repeated(self, input_node):
+ # if self.add_input_unless_repeated_in_document(input_node):
+ # return
+
def render_tab_set(self, tab_set: Optional[TabSet]):
if tab_set is None:
@@ -163,16 +193,17 @@ class TabbedHtmlTransform(SphinxPostTransform):
# TODO warn and continue if incorrect children
title, content = tab.children
# input <input checked="checked" id="id" type="radio">
- identity = self.get_unique_key()
+ identity = self.get_key_for(title)
input_node = tabbed_input(
"",
id=identity,
set_id=set_identity,
type="radio",
checked=(idx == selected_idx),
+ classes=["tabbed-input--hidden"],
)
input_node.source, input_node.line = tab.source, tab.line
- container += input_node
+ self.add_input_unless_repeated_in_document(input_node)
# label <label for="id">Title</label>
# TODO this actually has to be text only
label = tabbed_label("", *title.children, input_id=identity)
diff --git a/sphinx_panels/utils.py b/sphinx_panels/utils.py
index 19ebd7e..6bc9e3d 100644
--- a/sphinx_panels/utils.py
+++ b/sphinx_panels/utils.py
@@ -1,9 +1,10 @@
from ast import literal_eval
import re
-REGEX = re.compile(
+KEY_VALUE_REGEX = re.compile(
r'\s*(?:(?P<key>[a-zA-Z0-9_]+)\s*\=)?\s*(?P<value>".*"|[^,]+)\s*(?:,|$)'
)
+SLUGIFY_REGEX = re.compile(r"\W")
def eval_literal(string):
@@ -17,9 +18,14 @@ def eval_literal(string):
def string_to_func_inputs(text):
args = []
kwargs = {}
- for key, value in REGEX.findall(text):
+ for key, value in KEY_VALUE_REGEX.findall(text):
if key:
kwargs[key.strip()] = eval_literal(value.strip())
else:
args.append(eval_literal(value.strip()))
return args, kwargs
+
+
+def slugify(text):
+ print(text)
+ return SLUGIFY_REGEX.sub("-", text)
#tabbed--extension--reStructuredText:checked + * + * .tabbed-label[for="tabbed--extension--reStructuredText"] + .tabbed-content {
display: block;
}
#tabbed--extension--markdown:checked + * .tabbed-label[for="tabbed--extension--markdown"] + .tabbed-content {
display: block;
}
Works on the page I'm working with. It's ugly, but it works. :)
Bah, I give up. I'd much rather have 3 lines of JS than these 50 lines of Python-generating-CSS. :)
Is your feature request related to a problem? Please describe.
It'd be super cool to be able to use sphinx-panel's tab implementation to have synchronized tabs on a single page.
Describe the solution you'd like
This should be possible by having
input
elements on a per-title on a per-page basis. This would introduce some complexity (only add the inputs on the first tab with it, on a per-page basis) but it would allow us to basically deprecate the sphinx-tabs extension since we'll become feature-equivalent to it.Describe alternatives you've considered
Status quo, of recommending sphinx-tabs for synchronized tabs.
Additional context
I spent a lot of time thinking, and couldn't figure out a good reason, that someone might want tabs with the same title for a tab across multiple "tab groups", on the same page and not want them to be synchronized. :)