Skip to content
Merged
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
116 changes: 113 additions & 3 deletions assets/js/hugegraph-shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,82 @@
var restore = documentObject.querySelector('.hg-sidebar-restore');
var desktop = windowObject.matchMedia('(min-width: 768px)');

var panel = sidebar.querySelector('.td-shell-sidebar__panel');
// The native 16px panel edge cannot receive pointers while the sidebar is
// inert. Keep an equivalent pointer-only strip outside the inert subtree;
// the labelled navbar button remains the keyboard/touch equivalent.
var edge = documentObject.createElement('div');
edge.className = 'hg-sidebar-edge d-print-none';
edge.setAttribute('aria-hidden', 'true');
documentObject.body.appendChild(edge);
var closeTimer;
var pointerLockUntil = 0;
function dynamic() {
return desktop.matches &&
html.getAttribute('data-td-shell-sidebar') === 'collapsed';
}
function preview() {
if (!dynamic()) return;
windowObject.clearTimeout(closeTimer);
sidebar.classList.add('td-shell-sidebar--overlay');
sync();
}
function closePreview() {
windowObject.clearTimeout(closeTimer);
closeTimer = windowObject.setTimeout(function () {
if (!sidebar.contains(documentObject.activeElement)) {
sidebar.classList.remove('td-shell-sidebar--overlay');
sync();
}
}, 350);
}
edge.addEventListener('pointerenter', function (event) {
if (event.pointerType !== 'touch' && Date.now() >= pointerLockUntil) preview();
});
edge.addEventListener('pointerleave', closePreview);
if (restore) {
restore.addEventListener('pointerenter', function (event) {
if (event.pointerType !== 'touch' && Date.now() >= pointerLockUntil) preview();
});
restore.addEventListener('pointerleave', closePreview);
restore.addEventListener('keydown', function (event) {
if (event.key !== 'ArrowRight' || !dynamic()) return;
event.preventDefault();
preview();
var first = sidebar.querySelector('a[href], button');
if (first) first.focus();
});
}
if (panel) {
panel.addEventListener('pointerenter', function () {
windowObject.clearTimeout(closeTimer);
});
panel.addEventListener('focusout', closePreview);
panel.addEventListener('keydown', function (event) {
if (event.key !== 'Escape' || !dynamic()) return;
event.preventDefault();
if (restore) restore.focus();
sidebar.classList.remove('td-shell-sidebar--overlay');
sync();
});
}

function sync() {
var collapsed =
html.getAttribute('data-td-shell-sidebar') === 'collapsed';
var drawerOpen =
html.getAttribute('data-td-shell-drawer') === 'open';
var isolated = desktop.matches ? collapsed : !drawerOpen;
// OINK owns the persistent collapsed mode and pointer overlay. Keep
// focus inside an open preview safe when its native pointerleave fires.
if (desktop.matches && collapsed &&
!sidebar.classList.contains('td-shell-sidebar--overlay') &&
sidebar.contains(documentObject.activeElement)) {
sidebar.classList.add('td-shell-sidebar--overlay');
}
var overlay = sidebar.classList.contains('td-shell-sidebar--overlay');
var isolated = desktop.matches ? collapsed && !overlay : !drawerOpen;
edge.hidden = !desktop.matches || !collapsed;
if (restore) restore.setAttribute('aria-expanded', String(!isolated));
if (restore) restore.hidden = !desktop.matches || !collapsed;
sidebar.inert = isolated;
if (isolated) sidebar.setAttribute('aria-hidden', 'true');
Expand All @@ -150,17 +220,56 @@
attributes: true,
attributeFilter: ['data-td-shell-sidebar', 'data-td-shell-drawer'],
});
new MutationObserver(sync).observe(sidebar, { attributes: true, attributeFilter: ['class'] });
desktop.addEventListener('change', sync);
documentObject
.querySelectorAll('[data-td-shell-sidebar-toggle], [data-td-shell-drawer-close]')
.forEach(function (button) {
button.addEventListener('click', function () {
global.queueMicrotask(sync);
});
var hadFocus = documentObject.activeElement === button;
// Match OINK's cooldown so a newly visible trigger under the pointer
// does not immediately undo an explicit keyboard collapse.
pointerLockUntil = Date.now() + 150;
// Run after OINK's native click listener regardless of chunk order.
global.setTimeout(function () {
if (dynamic() && sidebar.contains(button) && restore) {
restore.hidden = false;
restore.focus();
sidebar.classList.remove('td-shell-sidebar--overlay');
}
sync();
// Both external restore controls disappear when pinned. Transfer
// their focus after making the sidebar operable; hover and clicks
// that did not focus a trigger must not steal unrelated focus.
if (hadFocus && desktop.matches && !dynamic() &&
!sidebar.contains(button) && button.offsetParent === null) {
var collapse = sidebar.querySelector('.td-shell-sidebar__collapse');
if (collapse) collapse.focus();
}
}, 0);
}, true);
});
sync();
}

function initScrollableTables(documentObject) {
documentObject.querySelectorAll('[data-td-asset-table-scroll]').forEach(function (region) {
if (region.dataset.hgTableScrollBound !== undefined) return;
region.dataset.hgTableScrollBound = '';
region.addEventListener('keydown', function (event) {
if (region.scrollWidth <= region.clientWidth) return;
var delta = 0;
if (event.key === 'ArrowRight') delta = 80;
else if (event.key === 'ArrowLeft') delta = -80;
else if (event.key === 'Home') delta = -region.scrollLeft;
else if (event.key === 'End') delta = region.scrollWidth - region.clientWidth - region.scrollLeft;
else return;
event.preventDefault();
region.scrollBy({ left: delta, behavior: 'smooth' });
});
});
}

function initSearchRetry(windowObject, documentObject) {
var root = documentObject.getElementById('td-shell-search');
if (!root) return;
Expand Down Expand Up @@ -324,6 +433,7 @@
initVersionSwitching(windowObject, documentObject);
initTreePersistence(windowObject, documentObject, config);
initSidebarIsolation(windowObject, documentObject);
initScrollableTables(documentObject);
initSearchRetry(windowObject, documentObject);
}

Expand Down
99 changes: 84 additions & 15 deletions assets/scss/_styles_project.scss
Original file line number Diff line number Diff line change
Expand Up @@ -491,28 +491,37 @@
display: none;
}

@media (min-width: 768px) {
[data-td-shell-sidebar='collapsed'] {
#td-shell-sidebar {
visibility: hidden;
pointer-events: none;
}
.hg-sidebar-edge {
position: fixed;
inset-inline-start: 0;
top: 64px;
bottom: 0;
width: 16px;
z-index: 121;
border-inline-start: 3px solid var(--bs-border-color);

// OINK v1.0 exposes a left-edge hover overlay after collapse. HugeGraph's
// explicit restore control replaces that hidden target entirely.
#td-shell-sidebar.td-shell-sidebar--overlay
.td-shell-sidebar__panel {
visibility: hidden;
transform: translateX(-100%);
pointer-events: none;
}
&:hover { border-inline-start-color: var(--bs-primary); }
}

@media (min-width: 768px) {
[data-td-shell-sidebar='collapsed'] {
.td-nav-util.hg-sidebar-restore:not([hidden]) {
display: inline-flex;
}
}
}

.hg-version-overflow {
color: var(--bs-body-color);
summary {
cursor: pointer;
padding: .5rem .75rem;
list-style: none;
&::-webkit-details-marker { display: none; }
&:focus-visible { outline: 2px solid var(--bs-primary); }
}
}

.hg-search-retry {
display: flex;
align-items: center;
Expand Down Expand Up @@ -645,7 +654,8 @@
gap: 0.35rem;

> a,
> button {
> button,
> .hg-version-overflow > a {
display: inline-flex;
align-items: center;
gap: 0.4rem;
Expand All @@ -660,12 +670,18 @@
}

> a[aria-current='page'],
> .hg-version-overflow > a[aria-current='page'],
> button[aria-pressed='true'] {
background: var(--bs-primary-bg-subtle);
color: var(--bs-primary-text-emphasis);
}
}

.hg-shell-mobile-utils__links > .hg-version-overflow > a {
margin-inline-end: 0.35rem;
margin-block-start: 0.35rem;
}

.hg-shell-mobile-utils__github {
display: inline-flex;
align-items: center;
Expand Down Expand Up @@ -834,3 +850,56 @@
}
}
}

// ASF download tables (layouts/_partials/asf-downloads.html): keep the cells
// on one line so the wide table scrolls inside td-asset-list__table-wrap
// instead of squeezing every column, and lay the release meta out inline.
.hg-asf-release {
// The focusable wrapper owns horizontal scrolling, not td-content's table.
.td-asset-list__table-wrap {
overflow-x: auto !important;
overflow-y: hidden;
}

.td-asset-list__table {
display: table !important;
width: max-content;
min-width: 100%;
overflow: visible !important;
}

.td-asset-list__table th,
.td-asset-list__table td {
white-space: nowrap;
}
}

.hg-asf-release__meta {
list-style: none;
padding-left: 0;
display: flex;
flex-wrap: wrap;
gap: 0.25rem 1.5rem;
}


// Local disclosure renders before any third-party AI resource is requested.
.hg-ai-consent {
width: calc(100% - 2rem);
max-width: 28rem;
padding: 1.5rem;
border: 1px solid var(--bs-border-color);
border-radius: .75rem;
background: var(--bs-body-bg);
color: var(--bs-body-color);
box-shadow: 0 .75rem 3rem rgba(0, 0, 0, .2);
&::backdrop { background: rgba(0, 0, 0, .4); }
h2 { font-size: 1.25rem; }
.hg-ai-consent-actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: .75rem;
margin-top: 1.25rem;
}
}
Loading
Loading