Skip to content

Commit 2991483

Browse files
authored
Added ASF-required links using drop-down menu and unified navigation (#1198)
* Added ASF-required footer links in doxfx.json - Added Foundation, Events, License, Thanks/(Sponsors), Security, Sponsorship, Privacy (Policy) links - Inserted into _appFooter in doxfx.json - Left 2024 copyright year unchanged * Revert footer changes in doxfx.json - Removed ASF links from _appFooter and preparing to move ASF links into the top navigation menu using dropdown. * Updated toc.yml with ASF menu entries - Added ASF Foundation, Events, License, Thanks, Security, Sponsorship, and Privacy Policy links to the apidocs/site menu. CSS and JS updates for the dropdown are still required. * Added ASF links to navigation and dropdown behavior to the site (not apidocs) - styles/custom.js included in head.tmpl.partial + head-content.tmpl.partial - main.css,custom.js: - ASF dropdown style (hover on dekstop, tap on mobile) with caret. - Attempts to make navbar stable across resize, zoom. (apidocs navbar will be addressed by the next commit) * Revert "Added ASF links to navigation and dropdown behavior to the site (not apidocs)" This reverts commit 3d8b026. * Added ASF dropdown & unify nav; introduce main.js (Site) - Desktop: hover dropdown; Mobile: accordion in hamburger - Carets and unified drop-down menu - Sticky header on desktop; scroll-away on mobile/compact - GitHub Fork Me ribbon safety margin - Added main.js and registered in head-content.tmpl.partial and head.tmpl.partial * Added ASF dropdown & unified nav; compact search overlay (Apidocs) - Desktop: hover dropdown; Mobile: accordion in hamburger - Carets and submenu unification - Sticky header on desktop; scorll-away on mobile, compact - GitHub Fork Me safety spacing - Replaced inline search with "Search..." trigger at 1200-1399px - It does not include the auto-updated docfx.global.json and docfx.global.subsite.json and any other auto-updated files. * Fixed the wrong indent style (tabs) error marked by the EditorConfig Checker for the .js files. * Restore wrap and rework CSS and JS for Apidocs - Reintroduced wrap, when the the navbar no longer fits into one line - CSS: Tried to clean it up by consolidating rules and made some changes due to how it reacted the wrapped navbar - JS: Now using matchMedia to detect and get the correct CSS mode and made some changes due to how it reacted the wrapped navbar * Restore wrap and rework CSS and JS for Site - Reintroduced wrap, when the the navbar no longer fits into one line - CSS: Tried to clean it up by consolidating rules and made some changes due to how it reacted the wrapped navbar - JS: Now using matchMedia to detect and get the correct CSS mode and made some changes due to how it reacted the wrapped navbar
1 parent f4cb566 commit 2991483

8 files changed

Lines changed: 1790 additions & 219 deletions

File tree

websites/apidocs/Templates/LuceneTemplateAssets/styles/main.css

Lines changed: 719 additions & 90 deletions
Large diffs are not rendered by default.

websites/apidocs/Templates/LuceneTemplateAssets/styles/main.js

Lines changed: 209 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,40 +17,220 @@
1717
* under the License.
1818
*/
1919

20-
$(function () {
20+
/* 1. Mode controller (desktop vs mobile) — use matchMedia to align with CSS */
21+
(function () {
22+
var ROOT_ID = 'autocollapse', COLLAPSED = 'collapsed', MODE = null;
23+
// Keep readable constant in case you want to change breakpoint in one place
24+
var DESKTOP_QUERY = '(min-width: 768px)';
25+
var scheduled = false;
2126

22-
var collapsed = 'collapsed';
23-
var navBarHeight = 80;
24-
var firstRun = true;
27+
function $(id) { return document.getElementById(id); }
28+
function root() { return $(ROOT_ID); }
29+
function nav() { return $('navbar'); }
30+
function toggleBtn() { return document.querySelector('#' + ROOT_ID + ' .navbar-toggle'); }
31+
function isPanelOpen() { var n = nav(); return !!(n && n.classList.contains('in')); }
2532

26-
renderAlerts();
33+
function setCollapseOpen(open) {
34+
var n = nav(), btn = toggleBtn(); if (!n) return;
35+
n.classList.add('collapse'); n.classList.remove('collapsing');
36+
if (open) {
37+
n.classList.add('in'); n.style.display = 'block'; n.style.height = '';
38+
n.setAttribute('aria-expanded', 'true');
39+
if (btn) { btn.classList.remove('collapsed'); btn.setAttribute('aria-expanded', 'true'); }
40+
} else {
41+
n.classList.remove('in'); n.style.display = ''; n.style.height = '';
42+
n.setAttribute('aria-expanded', 'false');
43+
if (btn) { btn.classList.add('collapsed'); btn.setAttribute('aria-expanded', 'false'); }
44+
}
45+
}
46+
47+
// Use matchMedia to determine desktop/mobile to match the CSS
48+
function isDesktop() {
49+
if (window.matchMedia) return window.matchMedia(DESKTOP_QUERY).matches;
50+
// Fallback: numeric check (rarely used)
51+
return (window.innerWidth || document.documentElement.clientWidth) >= 768;
52+
}
53+
54+
function decide() {
55+
return isDesktop() ? 'desktop' : 'mobile';
56+
}
57+
58+
function paint(mode) {
59+
var r = root(); if (!r) return;
60+
var isMobile = (mode === 'mobile');
61+
if (MODE !== mode) {
62+
r.classList.toggle(COLLAPSED, isMobile);
63+
setCollapseOpen(false); // never auto-open when switching
64+
// When switching to desktop, ensure any mobile-only open states are cleared
65+
if (!isMobile) {
66+
try {
67+
var opens = r.querySelectorAll('.nav-asf.is-open');
68+
for (var i = 0; i < opens.length; i++) opens[i].classList.remove('is-open');
69+
} catch (e) { /* defensive */ }
70+
}
71+
MODE = mode;
72+
return;
73+
}
74+
r.classList.toggle(COLLAPSED, isMobile);
75+
}
76+
77+
function recalc() {
78+
scheduled = false;
79+
if (root() && root().classList.contains(COLLAPSED) && isPanelOpen()) return;
80+
paint(decide());
81+
}
82+
function schedule() { if (scheduled) return; scheduled = true; requestAnimationFrame(recalc); }
83+
84+
if (document.readyState !== 'loading') schedule();
85+
else document.addEventListener('DOMContentLoaded', schedule);
86+
window.addEventListener('resize', schedule);
87+
window.addEventListener('orientationchange', schedule);
88+
window.addEventListener('load', function () { schedule(); setTimeout(schedule, 150); });
89+
})();
90+
91+
/* 2. ASF dropdown wiring (mobile click/tap-to-toggle; caret normalization) */
92+
(function () {
93+
function ready(fn) { if (document.readyState !== 'loading') fn(); else document.addEventListener('DOMContentLoaded', fn); }
94+
ready(function () {
95+
var r = document.getElementById('autocollapse'), wrap = document.getElementById('navbar');
96+
if (!r || !wrap) return;
2797

28-
function renderAlerts() {
29-
$('.lucene-block').addClass('alert alert-info');
98+
function first(li, tag) { tag = tag.toUpperCase(); for (var i = 0; i < li.children.length; i++) { var c = li.children[i]; if (c.tagName && c.tagName.toUpperCase() === tag) return c; } return null; }
99+
100+
function wire() {
101+
var ul = wrap.querySelector('ul.navbar-nav'); if (!ul) return false;
102+
var asf = null;
103+
for (var i = 0; i < ul.children.length; i++) {
104+
var li = ul.children[i]; if (li.tagName !== 'LI') continue;
105+
var a = first(li, 'A'); if (a && a.textContent.trim().toUpperCase() === 'ASF') { asf = li; break; }
106+
}
107+
if (!asf) return false;
108+
if (asf.getAttribute('data-asf-wired') === '1') return true;
109+
asf.setAttribute('data-asf-wired', '1'); asf.classList.add('nav-asf');
110+
111+
var stub = first(asf, 'SPAN'); if (stub && /\bexpand-stub\b/.test(stub.className)) stub.style.display = 'none';
112+
var aTop = first(asf, 'A');
113+
114+
// Hide Bootstrap caret, ensure exactly one .lucene-caret
115+
var old = aTop.querySelectorAll('.caret'); for (var k = 0; k < old.length; k++) old[k].style.display = 'none';
116+
var mine = aTop.querySelectorAll('.lucene-caret'); for (var x = 1; x < mine.length; x++) mine[x].remove();
117+
if (mine.length === 0) { var sp = document.createElement('span'); sp.className = 'lucene-caret'; aTop.appendChild(sp); }
118+
119+
// Find submenu UL, mark as asf-menu
120+
var sub = null;
121+
for (var j = 0; j < asf.children.length; j++) {
122+
var c = asf.children[j];
123+
if (c.tagName === 'UL' && /\bnav\b/.test(c.className) && /\blevel2\b/.test(c.className)) { sub = c; break; }
124+
}
125+
if (sub && sub.className.indexOf('asf-menu') === -1) sub.className += ' asf-menu';
126+
127+
// Mobile: click toggles .is-open; Desktop uses CSS :hover
128+
aTop.addEventListener('click', function (ev) {
129+
if (r.classList.contains('collapsed')) { ev.preventDefault(); ev.stopPropagation(); asf.classList.toggle('is-open'); }
130+
});
131+
aTop.addEventListener('mousedown', function (ev) { if (r.classList.contains('collapsed')) ev.stopPropagation(); });
132+
133+
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') asf.classList.remove('is-open'); });
134+
return true;
30135
}
31136

32-
//docfx has a hard coded value of 60px in height check for the nav bar
33-
//but our nav bar is taller so we need to work around this
34-
function fixAutoCollapseBug() {
35-
//remove docfx's handler
36-
$(window).off("resize");
37-
38-
autoCollapse();
39-
$(window).on('resize', autoCollapse);
40-
41-
function autoCollapse() {
42-
var navbar = $('#autocollapse');
43-
if (firstRun) {
44-
firstRun = false;
45-
setTimeout(autoCollapse, 310);
46-
}
47-
navbar.removeClass(collapsed);
48-
if (navbar.height() > navBarHeight) {
49-
navbar.addClass(collapsed);
50-
}
51-
}
137+
if (!wire()) {
138+
var obs = new MutationObserver(function () { if (wire()) obs.disconnect(); });
139+
obs.observe(wrap, { childList: true, subtree: true });
52140
}
141+
});
142+
})();
143+
144+
/* 3. Close hamburger when leaving mobile */
145+
(function () {
146+
var ROOT_ID = 'autocollapse';
147+
var DESKTOP_QUERY = '(min-width: 768px)';
148+
var lastMode = null, rafScheduled = false;
149+
150+
function $(sel, ctx) { return (ctx || document).querySelector(sel); }
151+
function $id(id) { return document.getElementById(id); }
152+
153+
function isDesktop() {
154+
if (window.matchMedia) return window.matchMedia(DESKTOP_QUERY).matches;
155+
return (window.innerWidth || document.documentElement.clientWidth) >= 768;
156+
}
157+
158+
function modeFromWidth() {
159+
return isDesktop() ? 'desktop' : 'mobile';
160+
}
161+
162+
function setCollapsed(root, collapsed) {
163+
if (!root) return;
164+
root.classList.toggle('collapsed', collapsed);
165+
}
166+
167+
function closeHamburger() {
168+
var nav = $id('navbar');
169+
var btn = $('#' + ROOT_ID + ' .navbar-toggle');
170+
if (nav) {
171+
nav.classList.add('collapse');
172+
nav.classList.remove('in', 'collapsing');
173+
nav.style.display = '';
174+
nav.style.height = '';
175+
nav.setAttribute('aria-expanded', 'false');
176+
}
177+
if (btn) {
178+
btn.classList.add('collapsed');
179+
btn.setAttribute('aria-expanded', 'false');
180+
}
181+
// also close ASF mobile submenu if it was open
182+
var asf = $('#' + ROOT_ID + ' .nav-asf');
183+
if (asf) asf.classList.remove('is-open');
184+
}
185+
186+
function apply() {
187+
rafScheduled = false;
188+
var root = $id(ROOT_ID);
189+
if (!root) return;
190+
191+
var mode = modeFromWidth();
192+
if (mode !== lastMode) {
193+
setCollapsed(root, mode === 'mobile');
194+
if (mode === 'desktop') closeHamburger();
195+
lastMode = mode;
196+
} else {
197+
setCollapsed(root, mode === 'mobile');
198+
}
199+
}
200+
201+
function schedule() { if (rafScheduled) return; rafScheduled = true; requestAnimationFrame(apply); }
202+
203+
if (document.readyState !== 'loading') schedule(); else document.addEventListener('DOMContentLoaded', schedule);
204+
window.addEventListener('load', function () { schedule(); setTimeout(schedule, 120); });
205+
206+
// on zoom/resize/orientation changes
207+
window.addEventListener('resize', schedule);
208+
window.addEventListener('orientationchange', schedule);
209+
})();
210+
211+
/* 4. Sticky header toggler (desktop only; non-sticky in mobile) */
212+
(function () {
213+
var ROOT_ID = 'autocollapse';
214+
215+
function updateSticky() {
216+
var head = document.querySelector('#wrapper > header') || document.querySelector('header');
217+
var auto = document.getElementById(ROOT_ID);
218+
if (!head || !auto) return;
219+
var desktop = !auto.classList.contains('collapsed');
220+
head.classList.toggle('is-sticky', desktop);
221+
}
222+
223+
// Run now and whenever the collapsed state changes / viewport changes
224+
if (document.readyState !== 'loading') updateSticky();
225+
else document.addEventListener('DOMContentLoaded', updateSticky);
226+
window.addEventListener('load', updateSticky);
227+
window.addEventListener('resize', updateSticky);
228+
window.addEventListener('orientationchange', updateSticky);
53229

54-
fixAutoCollapseBug();
230+
// Watch for class changes on #autocollapse
231+
var auto = document.getElementById(ROOT_ID);
232+
if (auto) {
233+
new MutationObserver(updateSticky).observe(auto, { attributes: true, attributeFilter: ['class'] });
234+
}
235+
})();
55236

56-
})

websites/apidocs/toc.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,19 @@
66
topicHref: ../../src/dotnet/tools/lucene-cli/docs/index.md
77
- name: Lucene.Net Website
88
href: https://lucenenet.apache.org/
9-
9+
- name: ASF
10+
items:
11+
- name: Foundation
12+
href: https://www.apache.org/
13+
- name: Events
14+
href: https://www.apache.org/events/
15+
- name: License
16+
href: https://www.apache.org/licenses/
17+
- name: Thanks
18+
href: https://www.apache.org/foundation/thanks.html
19+
- name: Security
20+
href: https://www.apache.org/security/
21+
- name: Sponsorship
22+
href: https://www.apache.org/foundation/sponsorship.html
23+
- name: Privacy Policy
24+
href: https://privacy.apache.org/policies/privacy-policy-public.html

websites/site/lucenetemplate/partials/head-content.tmpl.partial

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<link rel="stylesheet" href="{{_rel}}styles/docfx.vendor.min.css">
1313
<link rel="stylesheet" href="{{_rel}}styles/docfx.css">
1414
<link rel="stylesheet" href="{{_rel}}styles/main.css">
15+
<script src="{{_rel}}styles/main.js" defer></script>
1516
<meta property="docfx:navrel" content="{{_navRel}}">
1617
<meta property="docfx:tocrel" content="{{_tocRel}}">
1718
{{#_noindex}}<meta name="searchOption" content="noindex">{{/_noindex}}

websites/site/lucenetemplate/partials/head.tmpl.partial

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<link rel="stylesheet" href="{{_rel}}styles/docfx.vendor.min.css">
1313
<link rel="stylesheet" href="{{_rel}}styles/docfx.css">
1414
<link rel="stylesheet" href="{{_rel}}styles/main.css">
15+
<script src="{{_rel}}styles/main.js" defer></script>
1516
<meta property="docfx:navrel" content="{{_navRel}}">
1617
<meta property="docfx:tocrel" content="{{_tocRel}}">
1718
{{#_noindex}}<meta name="searchOption" content="noindex">{{/_noindex}}

0 commit comments

Comments
 (0)