Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions _includes/jekyll_vitepress/layout_end.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,38 @@
group.hidden = collections.indexOf(currentCollection) < 0;
});
}
// Show the version picker for the current product and mark the active version.
// The sidebar persists across Turbo navigation, so this re-runs per frame load.
function syncVersionPicker() {
var frameState = document.getElementById('vp-page-state');
if (!frameState) return;
var currentCollection = frameState.getAttribute('data-collection') || '';
document.querySelectorAll('.VPVersionPickerGroup').forEach(function (group) {
var collections = (group.getAttribute('data-collections') || '').split('|');
var isCurrent = collections.indexOf(currentCollection) >= 0;
group.hidden = !isCurrent;
if (!isCurrent) return;
// The _latest alias collection has no version link of its own; fall back
// to the version `latest` points at.
var target = currentCollection;
if (!group.querySelector('.VPVersionPickerItem[data-collection="' + currentCollection + '"]')) {
target = group.getAttribute('data-latest-collection') || '';
}
group.querySelectorAll('.VPVersionPickerItem').forEach(function (link) {
var active = link.getAttribute('data-collection') === target;
link.classList.toggle('is-active', active);
if (active) {
link.setAttribute('aria-current', 'true');
} else {
link.removeAttribute('aria-current');
}
});
});
}
document.addEventListener('turbo:frame-load', function (event) {
if (!event.target || event.target.id !== 'vp-content-frame') return;
syncSidebarNavGroups();
syncVersionPicker();
});
})();

Expand Down
2 changes: 2 additions & 0 deletions _includes/sidebar.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1">
<span class="visually-hidden" id="sidebar-aria-label">Sidebar Navigation</span>

{% include version-selector.html %}

{%- assign current_nav_collection_dir = page.collection | prepend: '_' | append: '/' -%}
{%- assign current_nav_page_subpath = page.relative_path | remove_first: current_nav_collection_dir | replace: '.md', '.html' | replace: '.markdown', '.html' -%}
{%- assign current_nav_base = page.url | remove: current_nav_page_subpath -%}
Expand Down
66 changes: 66 additions & 0 deletions _includes/version-selector.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{%- comment -%}
Per-product documentation version selector, rendered at the top of the sidebar.

Lists the current product's versions (newest first, per _data/products.yml),
marks the one being viewed, and badges the version that `latest` aliases. Links
point at each version's root (e.g. /openvox/9.x/); switching versions is a
deliberate, infrequent jump, and root links stay correct in the theme's
persistent (Turbo-navigated) sidebar without per-page rewriting. Keeping the
reader on the same page across a switch is a possible later enhancement.

Each qualifying product gets a block so the selector survives Turbo navigation
between products; the layout-end script shows the block matching the current
collection and marks the active version. A product is skipped when it is flagged
single_version (no numbered collections, e.g. OpenVox Containers) or has fewer
than two versions — there is nothing to switch between, so no picker is shown.
The wrapper is only emitted when at least one product qualifies, to avoid a
stray empty bordered box.
{%- endcomment -%}
{%- capture version_picker_groups -%}
{%- for product_entry in site.data.products -%}
{%- assign product_id = product_entry[0] -%}
{%- assign product = product_entry[1] -%}
{%- if product.single_version == true -%}{%- continue -%}{%- endif -%}
{%- if product.versions.size < 2 -%}{%- continue -%}{%- endif -%}

{%- comment -%} Collections this product owns: each numbered version, plus the _latest alias. {%- endcomment -%}
{%- assign latest_alias = product_id | append: '_latest' -%}
{%- assign collections = '' -%}
{%- assign latest_collection = '' -%}
{%- for version in product.versions -%}
{%- assign version_collection = version.collection | remove_first: '_' -%}
{%- assign collections = collections | append: version_collection | append: '|' -%}
{%- if version.id == product.latest -%}{%- assign latest_collection = version_collection -%}{%- endif -%}
{%- endfor -%}
{%- assign collections = collections | append: latest_alias -%}
{%- assign collection_list = collections | split: '|' -%}

{%- assign is_current_product = false -%}
{%- if collection_list contains page.collection -%}{%- assign is_current_product = true -%}{%- endif -%}

<div class="VPVersionPickerGroup" data-product="{{ product_id }}" data-collections="{{ collections }}" data-latest-collection="{{ latest_collection }}"{% unless is_current_product %} hidden{% endunless %}>
<span class="VPVersionPickerLabel">{{ product.label }} version</span>
<ul class="VPVersionPickerList">
{%- for version in product.versions -%}
{%- assign version_collection = version.collection | remove_first: '_' -%}
{%- assign is_active = false -%}
{%- if version_collection == page.collection -%}
{%- assign is_active = true -%}
{%- elsif page.collection == latest_alias and version.id == product.latest -%}
{%- assign is_active = true -%}
{%- endif -%}
<li>
<a class="VPVersionPickerItem{% if is_active %} is-active{% endif %}" href="{{ version.base }}" data-collection="{{ version_collection }}"{% if is_active %} aria-current="true"{% endif %}>
<span class="VPVersionPickerVersion">{{ version.label }}</span>
{%- if version.id == product.latest -%}<span class="VPVersionPickerBadge">latest</span>{%- endif -%}
</a>
</li>
{%- endfor -%}
</ul>
</div>
{%- endfor -%}
{%- endcapture -%}
{%- assign version_picker_groups = version_picker_groups | strip -%}
{%- if version_picker_groups != '' -%}
<div class="VPVersionPicker" id="vp-version-picker">{{ version_picker_groups }}</div>
{%- endif -%}
67 changes: 67 additions & 0 deletions assets/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,73 @@ table.resolution > tbody > tr:last-child > td:first-child {
text-align: right;
}

/* Per-product version picker at the top of the sidebar */
.VPVersionPicker {
margin-bottom: 1rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--vp-c-divider);
}

.VPVersionPickerLabel {
display: block;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.4px;
color: var(--vp-c-text-2);
margin-bottom: 0.4rem;
}

.VPVersionPickerList {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}

.VPVersionPickerItem {
display: inline-flex;
align-items: center;
gap: 0.3rem;
padding: 0.15rem 0.55rem;
border: 1px solid var(--vp-c-divider);
border-radius: 0.5rem;
font-size: 13px;
font-weight: 500;
color: var(--vp-c-text-2);
text-decoration: none;
transition: border-color 0.2s, background-color 0.2s, color 0.2s;
}

.VPVersionPickerItem:hover {
border-color: var(--vp-c-brand-1);
color: var(--vp-c-brand-1);
}

.VPVersionPickerItem.is-active {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-brand-soft);
color: var(--vp-c-brand-1);
}

.VPVersionPickerBadge {
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
padding: 0.05rem 0.3rem;
border-radius: 0.4rem;
background-color: var(--vp-c-brand-soft);
color: var(--vp-c-brand-1);
}

.VPVersionPickerItem.is-active .VPVersionPickerBadge {
background-color: var(--vp-c-brand-1);
color: var(--vp-c-white);
}

/* Highlight the active page more visibly */
.VPSidebarItem.level-0.is-active > .item .link > .text,
.VPSidebarItem.level-1.is-active > .item .link > .text,
Expand Down