Skip to content

Commit 6733784

Browse files
committed
feat: Add auto_generate_from for generic YAML-to-dropdown conversion
Add generic auto-generation of dropdown menus from arbitrary YAML structures. Features: - New 'auto_generate_from' field in dropdown config - Specify file path and optional key to navigate YAML - Special keys: 'fallback' or 'url' for clickable parent items - String values automatically become submenu items - Nested dicts are flattened (no intermediate menu levels) - Works with any YAML structure, not just POG docs Example usage: dropdowns: - title: "Docs" auto_generate_from: file: "path/to/data.yml" key: "pog_docs" # Optional: navigate to key links: - text: "Manual link" url: "https://..." # Auto-generated links appended Benefits: - Single source of truth in YAML - Automatic nested menu generation - Generic - works with any YAML structure - No code duplication
1 parent d9c4034 commit 6733784

1 file changed

Lines changed: 101 additions & 0 deletions

File tree

mkdocs_header_dropdown/plugin.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,76 @@ class HeaderDropdownPlugin(BasePlugin):
3535
('dropdowns', config_options.Type(list, default=[])),
3636
)
3737

38+
def _generate_links_from_yaml(self, data, parent_key=None):
39+
"""
40+
Recursively generate dropdown links from YAML structure.
41+
42+
Special keys:
43+
- 'fallback' or 'url': Used as the top-level URL (clickable parent)
44+
- Other string values: Create submenu items
45+
- Nested dicts: Flatten into parent's submenu (no intermediate level)
46+
47+
Args:
48+
data: Dict or string from YAML
49+
parent_key: Name of the parent key (used for display text)
50+
51+
Returns:
52+
List of link configurations or single link dict
53+
"""
54+
links = []
55+
56+
if isinstance(data, dict):
57+
# Check for URL at this level
58+
parent_url = data.get('fallback') or data.get('url')
59+
60+
# Collect submenu items from other keys
61+
submenu = []
62+
for key, value in data.items():
63+
if key in ('fallback', 'url'):
64+
continue # Skip these, used for parent URL
65+
66+
if isinstance(value, str):
67+
# Direct URL value - format the display name
68+
display_name = key.replace('_', ' ') # Convert Run2 -> Run 2
69+
submenu.append({
70+
'text': display_name,
71+
'url': value,
72+
'target': '_blank'
73+
})
74+
elif isinstance(value, dict):
75+
# Nested dict - flatten it one level up
76+
# Don't create an intermediate item, just add its children directly
77+
for nested_key, nested_value in value.items():
78+
if nested_key in ('fallback', 'url'):
79+
continue
80+
if isinstance(nested_value, str):
81+
# Format display name for era-specific docs
82+
display_name = nested_key.replace('-NanoAODv', ' (v').replace('NanoAODv', 'v')
83+
if not display_name.endswith(')') and 'v' in display_name:
84+
display_name += ')'
85+
submenu.append({
86+
'text': display_name,
87+
'url': nested_value,
88+
'target': '_blank'
89+
})
90+
91+
# Return parent link with submenu
92+
if parent_key:
93+
link = {
94+
'text': f"{parent_key} Docs" if parent_key != "PRO" else "PRO TWiki",
95+
'target': '_blank'
96+
}
97+
if parent_url:
98+
link['url'] = parent_url
99+
if submenu:
100+
link['submenu'] = submenu
101+
return link
102+
103+
# Top level - return all items as list
104+
return submenu
105+
106+
return links
107+
38108
def on_config(self, config: MkDocsConfig, **kwargs) -> MkDocsConfig:
39109
"""
40110
Add dropdown configuration to the MkDocs config's extra section.
@@ -54,6 +124,37 @@ def on_config(self, config: MkDocsConfig, **kwargs) -> MkDocsConfig:
54124
with open(config_file_path, 'r') as f:
55125
file_config = yaml.safe_load(f)
56126
if 'dropdowns' in file_config:
127+
# Check if any dropdown wants auto-generated links
128+
for dropdown in file_config['dropdowns']:
129+
# If a dropdown has auto_generate_from, load and generate links
130+
if 'auto_generate_from' in dropdown:
131+
auto_config = dropdown['auto_generate_from']
132+
yaml_file = auto_config.get('file')
133+
yaml_key = auto_config.get('key', None)
134+
135+
if yaml_file:
136+
yaml_file_path = os.path.join(config.docs_dir, '..', yaml_file)
137+
if os.path.exists(yaml_file_path):
138+
with open(yaml_file_path, 'r') as yf:
139+
yaml_data = yaml.safe_load(yf)
140+
141+
# Navigate to specified key if provided
142+
if yaml_key:
143+
for key_part in yaml_key.split('.'):
144+
yaml_data = yaml_data.get(key_part, {})
145+
146+
# Generate links from YAML structure
147+
if isinstance(yaml_data, dict):
148+
generated_links = []
149+
for top_key, top_value in yaml_data.items():
150+
link = self._generate_links_from_yaml(top_value, top_key)
151+
if link:
152+
generated_links.append(link)
153+
154+
# Merge with existing links
155+
dropdown['links'] = dropdown.get('links', []) + generated_links
156+
157+
del dropdown['auto_generate_from'] # Remove the marker
57158
dropdowns.extend(file_config['dropdowns'])
58159
else:
59160
raise FileNotFoundError(f"Config file not found: {config_file_path}")

0 commit comments

Comments
 (0)