Skip to content

Commit fd5fa69

Browse files
Merge issue #55 portal shell tokens
2 parents 4e73d46 + 08be0b3 commit fd5fa69

4 files changed

Lines changed: 463 additions & 188 deletions

File tree

frontend/index.html

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,27 @@
99
</head>
1010
<body data-view="library">
1111
<div class="mobile-topbar">
12-
<button id="mobile-menu-button" class="icon-button" type="button" aria-label="Open workspace"></button>
12+
<button id="mobile-menu-button" class="icon-button" type="button" aria-label="Open workspace" aria-controls="sidebar" aria-expanded="false">Menu</button>
1313
<span id="mobile-title">DataOps</span>
1414
<button id="mobile-work-bell-button" class="quiet-button work-bell-button mobile-work-bell-button" type="button" title="Notifications" aria-label="Notifications">
1515
<span class="work-bell-icon" aria-hidden="true">&#9737;</span>
1616
<span class="work-bell-count" aria-hidden="true">0</span>
1717
</button>
1818
<button id="mobile-new-button" class="quiet-button" type="button">New</button>
1919
</div>
20+
<div id="sidebar-scrim" class="sidebar-scrim" hidden></div>
2021

2122
<main class="app-shell">
2223
<div id="sidebar-resize" class="sidebar-resize" aria-hidden="true"></div>
2324
<aside id="sidebar" class="sidebar" aria-label="Workspace navigation">
2425
<header class="workspace-header">
25-
<button id="sidebar-close-button" class="icon-button mobile-only" type="button" aria-label="Close workspace">×</button>
26+
<button id="sidebar-close-button" class="icon-button mobile-only" type="button" aria-label="Close workspace">Close</button>
2627
<div class="workspace-mark" aria-hidden="true">D</div>
2728
<div class="workspace-title">
2829
<h1>DataOps</h1>
2930
<p>Operations docs and tasks</p>
3031
</div>
31-
<button id="sidebar-collapse-button" class="icon-button sidebar-toggle-button desktop-only" type="button" aria-label="Hide sidebar" title="Hide sidebar">«</button>
32+
<button id="sidebar-collapse-button" class="icon-button sidebar-toggle-button desktop-only" type="button" aria-label="Hide sidebar" title="Hide sidebar">Hide</button>
3233
</header>
3334

3435
<button id="operations-home-button" class="new-page-button operations-home-button" type="button">Operations home</button>
@@ -143,7 +144,7 @@ <h1>DataOps</h1>
143144
<section class="page-shell">
144145
<header class="page-toolbar">
145146
<div class="page-context">
146-
<button id="sidebar-expand-button" class="icon-button sidebar-toggle-button desktop-only" type="button" aria-label="Show sidebar" title="Show sidebar" hidden>»</button>
147+
<button id="sidebar-expand-button" class="icon-button sidebar-toggle-button desktop-only" type="button" aria-label="Show sidebar" title="Show sidebar" hidden>Show</button>
147148
<button id="back-button" class="quiet-button" type="button">← All docs</button>
148149
<span id="breadcrumb">All documents</span>
149150
<strong id="toolbar-title">Documents</strong>
@@ -155,7 +156,7 @@ <h1>DataOps</h1>
155156
<span class="work-bell-count" aria-hidden="true">0</span>
156157
</button>
157158
<button id="view-toggle-button" class="quiet-button" type="button" hidden>View</button>
158-
<button id="doc-pin-button" class="quiet-button" type="button" hidden title="Pin to sidebar"></button>
159+
<button id="doc-pin-button" class="quiet-button" type="button" hidden title="Pin to sidebar" aria-label="Pin to sidebar">Pin</button>
159160
<button id="doc-menu-button" class="quiet-button" type="button" hidden title="Doc actions"></button>
160161
<button id="discard-button" class="quiet-button" type="button" disabled>Discard</button>
161162
<button id="save-button" class="primary-button" type="button" disabled>Save</button>

frontend/src/app.js

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ function resolveApiBase() {
1313

1414
const body = document.body;
1515
const sidebar = document.querySelector("#sidebar");
16+
const sidebarScrim = document.querySelector("#sidebar-scrim");
1617
const sidebarResize = document.querySelector("#sidebar-resize");
1718
const mobileMenuButton = document.querySelector("#mobile-menu-button");
1819
const sidebarCloseButton = document.querySelector("#sidebar-close-button");
@@ -72,6 +73,7 @@ const helpClose = document.querySelector("#help-close");
7273
helpBackdrop.addEventListener("click", () => { helpModal.hidden = true; });
7374
helpClose.addEventListener("click", () => { helpModal.hidden = true; });
7475
const documentList = document.querySelector("#document-list");
76+
const pageShell = document.querySelector(".page-shell");
7577
const documentRowTemplate = document.querySelector("#document-row-template");
7678
const breadcrumb = document.querySelector("#breadcrumb");
7779
const toolbarTitle = document.querySelector("#toolbar-title");
@@ -134,6 +136,7 @@ let activeTaskPanelTask = null;
134136
let activeTaskPanelArtifacts = [];
135137
let activeBundlePanelId = null;
136138
let activeBundlePanelData = null;
139+
let lastSidebarOpener = null;
137140

138141
let allDocuments = [];
139142
let visibleDocuments = [];
@@ -154,6 +157,7 @@ const LIST_LIMIT = 120;
154157

155158
mobileMenuButton.addEventListener("click", openSidebar);
156159
sidebarCloseButton.addEventListener("click", closeSidebar);
160+
sidebarScrim?.addEventListener("click", closeSidebar);
157161
sidebarCollapseButton.addEventListener("click", () => setSidebarCollapsed(true));
158162
sidebarExpandButton.addEventListener("click", () => setSidebarCollapsed(false));
159163
themeToggleButton.addEventListener("click", () => setDarkMode(!body.classList.contains("dark")));
@@ -186,6 +190,7 @@ documentTitle.addEventListener("keydown", (event) => {
186190
}
187191
});
188192
window.addEventListener("resize", resizeDocumentTitle);
193+
window.addEventListener("resize", syncSidebarShellState);
189194
editor.addEventListener("input", () => {
190195
if (!currentDoc) return;
191196
storeDraft();
@@ -286,6 +291,7 @@ restoreDarkMode();
286291
restoreSidebarCollapsed();
287292
restoreSidebarWidth();
288293
attachSidebarResize();
294+
syncSidebarShellState();
289295

290296
function setDarkMode(on) {
291297
body.classList.toggle("dark", on);
@@ -447,8 +453,9 @@ function updatePinButton() {
447453
}
448454
docPinButton.hidden = false;
449455
const pinned = readPins().has(currentDoc.path);
450-
docPinButton.textContent = pinned ? "" : "";
456+
docPinButton.textContent = pinned ? "Pinned" : "Pin";
451457
docPinButton.title = pinned ? "Unpin from sidebar" : "Pin to sidebar";
458+
docPinButton.setAttribute("aria-label", pinned ? "Unpin from sidebar" : "Pin to sidebar");
452459
docPinButton.classList.toggle("is-pinned", pinned);
453460
}
454461

@@ -471,7 +478,7 @@ function renderPinned() {
471478
label.textContent = doc.title || basename(path);
472479
const star = document.createElement("span");
473480
star.className = "recent-row-when";
474-
star.textContent = "";
481+
star.textContent = "Pinned";
475482
btn.append(label, star);
476483
btn.addEventListener("click", () => openDocument(path));
477484
return btn;
@@ -3824,8 +3831,45 @@ function setView(view) {
38243831

38253832
const FOCUSABLE_SELECTOR = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
38263833

3834+
function isMobileShell() {
3835+
return window.matchMedia("(max-width: 820px)").matches;
3836+
}
3837+
3838+
function syncSidebarShellState() {
3839+
const open = body.classList.contains("sidebar-open");
3840+
if (!isMobileShell()) {
3841+
body.classList.remove("sidebar-open");
3842+
sidebarScrim.hidden = true;
3843+
mobileMenuButton.setAttribute("aria-expanded", "false");
3844+
sidebar.removeAttribute("role");
3845+
sidebar.removeAttribute("aria-modal");
3846+
sidebar.removeAttribute("aria-hidden");
3847+
pageShell.inert = false;
3848+
if (mobileWorkBellButton) mobileWorkBellButton.inert = false;
3849+
mobileNewButton.inert = false;
3850+
document.removeEventListener("keydown", handleSidebarKeydown);
3851+
return;
3852+
}
3853+
sidebarScrim.hidden = !open;
3854+
mobileMenuButton.setAttribute("aria-expanded", open ? "true" : "false");
3855+
if (open) {
3856+
sidebar.setAttribute("role", "dialog");
3857+
sidebar.setAttribute("aria-modal", "true");
3858+
sidebar.removeAttribute("aria-hidden");
3859+
} else {
3860+
sidebar.removeAttribute("role");
3861+
sidebar.removeAttribute("aria-modal");
3862+
sidebar.setAttribute("aria-hidden", "true");
3863+
}
3864+
pageShell.inert = open;
3865+
if (mobileWorkBellButton) mobileWorkBellButton.inert = open;
3866+
mobileNewButton.inert = open;
3867+
}
3868+
38273869
function openSidebar() {
3870+
lastSidebarOpener = document.activeElement instanceof HTMLElement ? document.activeElement : mobileMenuButton;
38283871
body.classList.add("sidebar-open");
3872+
syncSidebarShellState();
38293873
document.addEventListener("keydown", handleSidebarKeydown);
38303874
const first = sidebar.querySelector(FOCUSABLE_SELECTOR);
38313875
first?.focus();
@@ -3834,10 +3878,11 @@ function openSidebar() {
38343878
function closeSidebar() {
38353879
if (!body.classList.contains("sidebar-open")) return;
38363880
body.classList.remove("sidebar-open");
3881+
syncSidebarShellState();
38373882
document.removeEventListener("keydown", handleSidebarKeydown);
3838-
if (sidebar.contains(document.activeElement)) {
3839-
mobileMenuButton.focus();
3840-
}
3883+
const focusTarget = lastSidebarOpener?.isConnected ? lastSidebarOpener : mobileMenuButton;
3884+
if (focusTarget instanceof HTMLElement) focusTarget.focus();
3885+
lastSidebarOpener = null;
38413886
}
38423887

38433888
function handleSidebarKeydown(event) {

0 commit comments

Comments
 (0)